diff --git a/Makefile b/Makefile index 5a6c10ab..79a66c4b 100644 --- a/Makefile +++ b/Makefile @@ -108,3 +108,11 @@ package : docs : $(ROOT)docs/extract.pl $(ROOT)bjforth/src/main/forth/bjForth.forth $(ROOT)docs/bjForth.forth.md + +#################################################################################################### + +.PHONY : run + +run : bmakelib.error-if-blank( VERSION ) +run : gradle( shadowJar ) + cat $(root.forth)bjForth.forth - | java -jar $(root.build)bjForth-$(VERSION).jar diff --git a/bjforth/src/main/java/bjforth/primitives/ATLANGLE.java b/bjforth/src/main/java/bjforth/primitives/ATLANGLE.java index 7d22d2a9..74dfdf66 100644 --- a/bjforth/src/main/java/bjforth/primitives/ATLANGLE.java +++ b/bjforth/src/main/java/bjforth/primitives/ATLANGLE.java @@ -88,13 +88,27 @@ public void execute(Machine machine) { if (" ".equals(s) || "\t".equals(s) || "\n".equals(s)) { // Ignore whitespace } else if (",".equals(s)) { - result.parameterTypes.add(ClassCache.forName(parameterType.toString())); + var rawParamType = parameterType.toString(); + if (rawParamType.endsWith("[]")) { + var paramType = rawParamType.replace("[]", ""); + result.parameterTypes.add(ClassCache.forNameArray(paramType)); + } else if (rawParamType.isEmpty()) { + // Ignore + } else { + result.parameterTypes.add(ClassCache.forName(rawParamType)); + } parameterType = new StringBuilder(); } else if (".".equals(s)) { state = State.IN_MAYBE_VARARG; } else if (")".equals(s)) { - if (!parameterType.isEmpty()) { - result.parameterTypes.add(ClassCache.forName(parameterType.toString())); + var rawParamType = parameterType.toString(); + if (rawParamType.endsWith("[]")) { + var paramType = rawParamType.replace("[]", ""); + result.parameterTypes.add(ClassCache.forNameArray(paramType)); + } else if (rawParamType.isEmpty()) { + // Ignore + } else { + result.parameterTypes.add(ClassCache.forName(rawParamType)); } state = State.IN_ARITY; } else { diff --git a/bjforth/src/main/java/bjforth/primitives/COMMALANGLE.java b/bjforth/src/main/java/bjforth/primitives/COMMALANGLE.java index 5b8a5551..77ddc338 100644 --- a/bjforth/src/main/java/bjforth/primitives/COMMALANGLE.java +++ b/bjforth/src/main/java/bjforth/primitives/COMMALANGLE.java @@ -98,12 +98,26 @@ public void execute(Machine machine) { if (" ".equals(s) || "\t".equals(s) || "\n".equals(s)) { // Ignore whitespace } else if (",".equals(s)) { - result.parameterTypes.add(ClassCache.forName(parameterType.toString())); + var rawParamType = parameterType.toString(); + if (rawParamType.endsWith("[]")) { + var paramType = rawParamType.replace("[]", ""); + result.parameterTypes.add(ClassCache.forNameArray(paramType)); + } else { + result.parameterTypes.add(ClassCache.forName(rawParamType)); + } parameterType = new StringBuilder(); } else if (".".equals(s)) { state = State.IN_MAYBE_VARARG; } else if (")".equals(s)) { - result.parameterTypes.add(ClassCache.forName(parameterType.toString())); + var rawParamType = parameterType.toString(); + if (rawParamType.endsWith("[]")) { + var paramType = rawParamType.replace("[]", ""); + result.parameterTypes.add(ClassCache.forNameArray(paramType)); + } else if (rawParamType.isEmpty()) { + // Ignore + } else { + result.parameterTypes.add(ClassCache.forName(rawParamType)); + } state = State.IN_ARITY; } else { parameterType.append(s); @@ -111,7 +125,13 @@ public void execute(Machine machine) { break; case State.IN_MAYBE_VARARG: if (".".equals(s)) { - result.parameterTypes.add(ClassCache.forNameVararg(parameterType.toString())); + var rawParamType = parameterType.toString(); + if (rawParamType.endsWith("[]")) { + var paramType = rawParamType.replace("[]", ""); + result.parameterTypes.add(ClassCache.forNameArray(paramType)); + } else { + result.parameterTypes.add(ClassCache.forNameVararg(rawParamType)); + } state = State.IN_VARARG; } else { parameterType.append("."); diff --git a/bjforth/src/main/java/bjforth/primitives/DOTLANGLE.java b/bjforth/src/main/java/bjforth/primitives/DOTLANGLE.java index c9794235..d81e501b 100644 --- a/bjforth/src/main/java/bjforth/primitives/DOTLANGLE.java +++ b/bjforth/src/main/java/bjforth/primitives/DOTLANGLE.java @@ -79,14 +79,26 @@ public void execute(Machine machine) { break; case State.IN_PARAM_TYPE: if (")".equals(s)) { - if (!parameterType.isEmpty()) { - result.parameterTypes.add(ClassCache.forName(parameterType.toString())); + var rawParamType = parameterType.toString(); + if (rawParamType.endsWith("[]")) { + var paramType = rawParamType.replace("[]", ""); + result.parameterTypes.add(ClassCache.forNameArray(paramType)); + } else if (rawParamType.isEmpty()) { + // Ignore + } else { + result.parameterTypes.add(ClassCache.forName(rawParamType)); } state = State.IN_ARITY; } else if (" ".equals(s) || "\t".equals(s) || "\n".equals(s)) { // Ignore whitespace } else if (",".equals(s)) { - result.parameterTypes.add(ClassCache.forName(parameterType.toString())); + var rawParamType = parameterType.toString(); + if (rawParamType.endsWith("[]")) { + var paramType = rawParamType.replace("[]", ""); + result.parameterTypes.add(ClassCache.forNameArray(paramType)); + } else { + result.parameterTypes.add(ClassCache.forName(rawParamType)); + } parameterType = new StringBuilder(); } else if (".".equals(s)) { state = State.IN_MAYBE_VARARG; diff --git a/bjforth/src/main/java/bjforth/primitives/RANGLEDOT.java b/bjforth/src/main/java/bjforth/primitives/RANGLEDOT.java index 8e017fc9..629279af 100644 --- a/bjforth/src/main/java/bjforth/primitives/RANGLEDOT.java +++ b/bjforth/src/main/java/bjforth/primitives/RANGLEDOT.java @@ -39,7 +39,8 @@ public void execute(Machine machine) { var paramTypes = new Class>[methodDescriptor.parameterTypes.size()]; methodDescriptor.parameterTypes.toArray(paramTypes); var method = - MethodUtils.getAccessibleMethod(target.getClass(), methodDescriptor.name, paramTypes); + MethodUtils.getMatchingAccessibleMethod( + target.getClass(), methodDescriptor.name, paramTypes); if (method == null) { throw new MachineException( "No such method found: %s/%d".formatted(methodDescriptor.name, methodDescriptor.arity)); diff --git a/bjforth/src/main/java/bjforth/primitives/lib/ClassCache.java b/bjforth/src/main/java/bjforth/primitives/lib/ClassCache.java index 1350a49a..83117a50 100644 --- a/bjforth/src/main/java/bjforth/primitives/lib/ClassCache.java +++ b/bjforth/src/main/java/bjforth/primitives/lib/ClassCache.java @@ -149,4 +149,67 @@ public static Class> forNameVararg(String typeName) { } } } + + /** + * Tries to load the array class from cache if exists. + * + *
If the type name contains '.', tries to look for the array with type name verbatim. + * + *
Otherwise looks in 'java.lang', 'java.util' and 'java.io' in order. + * + *
Throws if the type cannot be found.
+ *
+ * @throws MachineException
+ */
+ public static Class> forNameArray(String elementTypeName) {
+ if (elementTypeName.contains(".")) {
+ var arrayTypeName = "[L%s;".formatted(elementTypeName);
+ try {
+ if (cache.containsKey(arrayTypeName)) {
+ return cache.get(arrayTypeName);
+ } else {
+ var clazz = Class.forName(arrayTypeName);
+ cache.put(arrayTypeName, clazz);
+ return clazz;
+ }
+ } catch (ClassNotFoundException e) {
+ throw new MachineException(e.getMessage());
+ }
+ } else {
+ try {
+ var arrayTypeName = "[Ljava.lang.%s;".formatted(elementTypeName);
+ if (cache.containsKey(arrayTypeName)) {
+ return cache.get(arrayTypeName);
+ } else {
+ var clazz = Class.forName(arrayTypeName);
+ cache.put(arrayTypeName, clazz);
+ return clazz;
+ }
+ } catch (ClassNotFoundException _e) {
+ try {
+ var arrayTypeName = "[Ljava.util.%s;".formatted(elementTypeName);
+ if (cache.containsKey(arrayTypeName)) {
+ return cache.get(arrayTypeName);
+ } else {
+ var clazz = Class.forName(arrayTypeName);
+ cache.put(arrayTypeName, clazz);
+ return clazz;
+ }
+ } catch (ClassNotFoundException _ex) {
+ try {
+ var arrayTypeName = "[Ljava.io.%s;".formatted(elementTypeName);
+ if (cache.containsKey(arrayTypeName)) {
+ return cache.get(arrayTypeName);
+ } else {
+ var clazz = Class.forName(arrayTypeName);
+ cache.put(arrayTypeName, clazz);
+ return clazz;
+ }
+ } catch (ClassNotFoundException e) {
+ throw new MachineException(e.getMessage());
+ }
+ }
+ }
+ }
+ }
}
diff --git a/bjforth/src/test/java/bjforth/primitives/ATLANGLETest.java b/bjforth/src/test/java/bjforth/primitives/ATLANGLETest.java
index 190ba3cb..36f2d53c 100644
--- a/bjforth/src/test/java/bjforth/primitives/ATLANGLETest.java
+++ b/bjforth/src/test/java/bjforth/primitives/ATLANGLETest.java
@@ -101,4 +101,31 @@ void worksOkUnqualified() {
assertThat(machine.getMemoryAt(Variables.get("HERE").getAddress())).isEqualTo(HEREvalue + 2);
}
+
+ @Test
+ void worksOkArrayParameters() {
+ // GIVEN
+ var str = "String(File[], String[])/2 ";
+ var inputStream = new ByteArrayInputStream(str.getBytes());
+ System.setIn(inputStream);
+
+ var ATLANGLEaddr = getPrimitiveAddress("@<");
+ var actualState = aMachineState().withInstrcutionPointer(ATLANGLEaddr).build();
+ var machine = aMachine().withState(actualState).build();
+ var HEREvalue = (Integer) machine.getMemoryAt(Variables.get("HERE").getAddress());
+
+ // WHEN
+ machine.step();
+
+ // THEN
+ assertThat(machine.getMemoryAt(HEREvalue)).isEqualTo(getPrimitiveAddress("LIT"));
+
+ var actualResult = (MethodDescriptor) machine.getMemoryAt(HEREvalue + 1);
+ assertThat(actualResult.clazz).isEqualTo(String.class);
+ assertThat(actualResult.arity).isEqualTo(2);
+ assertThat(actualResult.parameterTypes).isEqualTo(List.of(File[].class, String[].class));
+ assertThat(actualResult.varargFromArgumentNo).isEqualTo(-1);
+
+ assertThat(machine.getMemoryAt(Variables.get("HERE").getAddress())).isEqualTo(HEREvalue + 2);
+ }
}
diff --git a/bjforth/src/test/java/bjforth/primitives/COMMALANGLETest.java b/bjforth/src/test/java/bjforth/primitives/COMMALANGLETest.java
index a4cc310d..c095f714 100644
--- a/bjforth/src/test/java/bjforth/primitives/COMMALANGLETest.java
+++ b/bjforth/src/test/java/bjforth/primitives/COMMALANGLETest.java
@@ -101,4 +101,31 @@ void worksOkUnqualified() {
assertThat(machine.getMemoryAt(Variables.get("HERE").getAddress())).isEqualTo(HEREvalue + 2);
}
+
+ @Test
+ void worksOkArrayParameter() {
+ var str = "String/format(File[], String[])/2 ";
+ var inputStream = new ByteArrayInputStream(str.getBytes());
+ System.setIn(inputStream);
+
+ var COMMALANGLEaddr = getPrimitiveAddress(",<");
+ var actualState = aMachineState().withInstrcutionPointer(COMMALANGLEaddr).build();
+ var machine = aMachine().withState(actualState).build();
+ var HEREvalue = (Integer) machine.getMemoryAt(Variables.get("HERE").getAddress());
+
+ // WHEN
+ machine.step();
+
+ // THEN
+ assertThat(machine.getMemoryAt(HEREvalue)).isEqualTo(getPrimitiveAddress("LIT"));
+
+ var actualResult = (MethodDescriptor) machine.getMemoryAt(HEREvalue + 1);
+ assertThat(actualResult.target).isEqualTo(String.class);
+ assertThat(actualResult.arity).isEqualTo(2);
+ assertThat(actualResult.parameterTypes).isEqualTo(List.of(File[].class, String[].class));
+ assertThat(actualResult.name).isEqualTo("format");
+ assertThat(actualResult.varargFromArgumentNo).isEqualTo(-1);
+
+ assertThat(machine.getMemoryAt(Variables.get("HERE").getAddress())).isEqualTo(HEREvalue + 2);
+ }
}
diff --git a/bjforth/src/test/java/bjforth/primitives/DOTLANGLETest.java b/bjforth/src/test/java/bjforth/primitives/DOTLANGLETest.java
index 2a91162c..3bb59b6a 100644
--- a/bjforth/src/test/java/bjforth/primitives/DOTLANGLETest.java
+++ b/bjforth/src/test/java/bjforth/primitives/DOTLANGLETest.java
@@ -101,4 +101,30 @@ void worksOkUnqualified() {
assertThat(machine.getMemoryAt(Variables.get("HERE").getAddress())).isEqualTo(HEREvalue + 2);
}
+
+ @Test
+ void worksOkArrayParameter() {
+ var str = "format(List[], File[])/2 ";
+ var inputStream = new ByteArrayInputStream(str.getBytes());
+ System.setIn(inputStream);
+
+ var DOTLANGLEaddr = getPrimitiveAddress(".<");
+ var actualState = aMachineState().withInstrcutionPointer(DOTLANGLEaddr).build();
+ var machine = aMachine().withState(actualState).build();
+ var HEREvalue = (Integer) machine.getMemoryAt(Variables.get("HERE").getAddress());
+
+ // WHEN
+ machine.step();
+
+ // THEN
+ assertThat(machine.getMemoryAt(HEREvalue)).isEqualTo(getPrimitiveAddress("LIT"));
+
+ var actualResult = (MethodDescriptor) machine.getMemoryAt(HEREvalue + 1);
+ assertThat(actualResult.name).isEqualTo("format");
+ assertThat(actualResult.varargFromArgumentNo).isEqualTo(-1);
+ assertThat(actualResult.parameterTypes).isEqualTo(List.of(List[].class, File[].class));
+ assertThat(actualResult.arity).isEqualTo(2);
+
+ assertThat(machine.getMemoryAt(Variables.get("HERE").getAddress())).isEqualTo(HEREvalue + 2);
+ }
}
diff --git a/bjforth/src/test/java/bjforth/primitives/RANGLEDOTTest.java b/bjforth/src/test/java/bjforth/primitives/RANGLEDOTTest.java
index dd3ada8c..65690117 100644
--- a/bjforth/src/test/java/bjforth/primitives/RANGLEDOTTest.java
+++ b/bjforth/src/test/java/bjforth/primitives/RANGLEDOTTest.java
@@ -25,6 +25,7 @@
import static bjforth.machine.ParameterStackBuilder.aParameterStack;
import bjforth.primitives.DOTLANGLE.MethodDescriptor;
+import java.util.ArrayList;
import java.util.List;
import org.apache.commons.lang3.RandomUtils;
import org.junit.jupiter.api.DisplayName;
@@ -112,4 +113,32 @@ void voidReturn() {
// THEN
assertThat(actualState).hasParameterStackEqualTo(aParameterStack().with((Object) null).build());
}
+
+ @Test
+ void worksOkInnerClasses() {
+ // GIVEN
+ var DOTDOTaddr = getPrimitiveAddress(">.");
+ // var target = List.of(10, 20);
+ var target = new ArrayList