diff --git a/.classpath b/.classpath
deleted file mode 100644
index 18d70f0..0000000
--- a/.classpath
+++ /dev/null
@@ -1,6 +0,0 @@
-
-
-
-
-
-
diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
new file mode 100644
index 0000000..80dec7e
--- /dev/null
+++ b/.github/workflows/build.yml
@@ -0,0 +1,14 @@
+name: build
+on: [push]
+jobs:
+ build:
+ runs-on: ubuntu-latest
+
+ steps:
+ - uses: actions/checkout@v2
+ - name: Set up JDK 21
+ uses: actions/setup-java@v1
+ with:
+ java-version: 21
+ - name: Build with Maven
+ run: mvn --batch-mode --update-snapshots verify
diff --git a/.gitignore b/.gitignore
index e2d08a6..9c989c3 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,3 +1,8 @@
-*.class
-*.jar
+.classpath
+.project
+.settings
+.DS_Store
+bin
+build
+target
*~
diff --git a/.project b/.project
deleted file mode 100644
index de77942..0000000
--- a/.project
+++ /dev/null
@@ -1,17 +0,0 @@
-
-
- PAL
-
-
-
-
-
- org.eclipse.jdt.core.javabuilder
-
-
-
-
-
- org.eclipse.jdt.core.javanature
-
-
diff --git a/.settings/org.eclipse.jdt.core.prefs b/.settings/org.eclipse.jdt.core.prefs
deleted file mode 100644
index b1aef03..0000000
--- a/.settings/org.eclipse.jdt.core.prefs
+++ /dev/null
@@ -1,12 +0,0 @@
-#Wed Mar 14 14:37:03 CST 2012
-eclipse.preferences.version=1
-org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
-org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.6
-org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve
-org.eclipse.jdt.core.compiler.compliance=1.6
-org.eclipse.jdt.core.compiler.debug.lineNumber=generate
-org.eclipse.jdt.core.compiler.debug.localVariable=generate
-org.eclipse.jdt.core.compiler.debug.sourceFile=generate
-org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
-org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
-org.eclipse.jdt.core.compiler.source=1.6
diff --git a/CHANGELOG.md b/CHANGELOG.md
new file mode 100644
index 0000000..eb0b08b
--- /dev/null
+++ b/CHANGELOG.md
@@ -0,0 +1,12 @@
+# Changelog
+
+This project adheres to [Semantic
+Versioning](https://semver.org/spec/v2.0.0.html).
+
+## Release 0.3 (2024-03-31)
+### Changed
+- Migrated to Maven for builds. This included setting up the test
+ suite via JUnit. [#10](https://github.com/paulhoadley/pal/issues/10)
+- Reduced reliance on `System.exit()` (outside of `main()`), largely
+ to facilitate
+ testing. [#11](https://github.com/paulhoadley/pal/issues/11)
diff --git a/README.markdown b/README.md
similarity index 86%
rename from README.markdown
rename to README.md
index 71f21f1..3457b8d 100644
--- a/README.markdown
+++ b/README.md
@@ -1,3 +1,6 @@
+
+[](https://opensource.org/licenses/BSD-3-Clause)
+
The PAL Abstract Machine—An implementation in Java
==================================================
diff --git a/pom.xml b/pom.xml
new file mode 100644
index 0000000..db8975b
--- /dev/null
+++ b/pom.xml
@@ -0,0 +1,97 @@
+
+ 4.0.0
+ net.logicsquad
+ pal
+ 0.3
+ jar
+ PAL
+ The PAL Abstract Machine—An implementation in Java.
+ 2002
+
+
+ Logic Squad
+ https://logicsquad.net/
+
+
+
+ UTF-8
+ UTF-8
+ 21
+ 21
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-jar-plugin
+
+
+
+ true
+ net.logicsquad.pal.PAL
+
+
+
+
+
+ maven-surefire-plugin
+ 3.2.5
+
+ 1
+ false
+
+
+
+ org.apache.maven.plugins
+ maven-javadoc-plugin
+ 3.6.3
+
+ -Xdoclint:none
+
+ private
+
+
+
+ build-javadocs
+ package
+
+ javadoc
+
+
+
+
+
+ maven-project-info-reports-plugin
+ 3.5.0
+
+
+
+
+
+
+
+ org.junit
+ junit-bom
+ 5.10.2
+ pom
+ import
+
+
+
+
+
+
+ org.apache.logging.log4j
+ log4j-slf4j2-impl
+ 2.22.1
+ test
+
+
+ org.junit.jupiter
+ junit-jupiter
+ test
+
+
+
diff --git a/src/net/logicsquad/pal/Code.java b/src/main/java/net/logicsquad/pal/Code.java
similarity index 100%
rename from src/net/logicsquad/pal/Code.java
rename to src/main/java/net/logicsquad/pal/Code.java
diff --git a/src/net/logicsquad/pal/Data.java b/src/main/java/net/logicsquad/pal/Data.java
similarity index 100%
rename from src/net/logicsquad/pal/Data.java
rename to src/main/java/net/logicsquad/pal/Data.java
diff --git a/src/net/logicsquad/pal/DataStack.java b/src/main/java/net/logicsquad/pal/DataStack.java
similarity index 100%
rename from src/net/logicsquad/pal/DataStack.java
rename to src/main/java/net/logicsquad/pal/DataStack.java
diff --git a/src/net/logicsquad/pal/Mnemonic.java b/src/main/java/net/logicsquad/pal/Mnemonic.java
similarity index 100%
rename from src/net/logicsquad/pal/Mnemonic.java
rename to src/main/java/net/logicsquad/pal/Mnemonic.java
diff --git a/src/net/logicsquad/pal/PAL.java b/src/main/java/net/logicsquad/pal/PAL.java
similarity index 90%
rename from src/net/logicsquad/pal/PAL.java
rename to src/main/java/net/logicsquad/pal/PAL.java
index 771089c..41c7cc7 100644
--- a/src/net/logicsquad/pal/PAL.java
+++ b/src/main/java/net/logicsquad/pal/PAL.java
@@ -1,9 +1,11 @@
package net.logicsquad.pal;
import java.io.BufferedReader;
+import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
+import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.PushbackReader;
import java.util.ArrayList;
@@ -52,6 +54,18 @@ public class PAL {
private static final int typeMismatch = 3;
private static final int reachedEOF = 4;
+ private enum ExitStatus {
+ NORMAL(0),
+ ABNORMAL(1);
+
+ private final int exitCode;
+
+ private ExitStatus(int exitCode) {
+ this.exitCode = exitCode;
+ return;
+ }
+ }
+
/**
* Main method for command line operation.
*
@@ -67,19 +81,25 @@ public static void main(String[] args) {
}
// Make a machine and load the code.
- PAL machine = new PAL();
// Execute.
+ ExitStatus status = null;
try {
- machine.execute();
+ PAL machine = new PAL(new FileInputStream(filename));
+ status = machine.execute();
} catch (OutOfMemoryError e) {
System.err.println(e.getMessage());
System.exit(1);
} catch (IndexOutOfBoundsException e) {
System.err.println(e.getMessage());
System.exit(1);
+ } catch (FileNotFoundException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+ if (status == null || status == ExitStatus.ABNORMAL) {
+ System.exit(ExitStatus.ABNORMAL.exitCode);
}
-
return;
}
@@ -90,13 +110,13 @@ public static void main(String[] args) {
* lexical analysis of the source file is quite rigid. Any deviation from
* the prescribed format for source files causes the machine to stop.
*/
- public PAL() {
+ PAL(InputStream is) {
// Create the code memory.
codeMem = new ArrayList(CODESIZE);
dataStack = new DataStack(DATASIZE);
try {
- BufferedReader br = new BufferedReader(new FileReader(filename));
+ BufferedReader br = new BufferedReader(new InputStreamReader(is));
int lineno = 1;
String line = br.readLine();
String mnemonic = "";
@@ -108,7 +128,7 @@ public PAL() {
if (lineno > CODESIZE) {
System.err.println("Exceeded code storage limit at line "
+ lineno);
- die(1);
+ System.exit(ExitStatus.ABNORMAL.exitCode);
}
st = new StringTokenizer(line);
@@ -135,16 +155,16 @@ public PAL() {
if (second instanceof String) {
System.err.println("Unrecognised second operand"
+ " on line " + lineno);
- die(1);
+ System.exit(ExitStatus.ABNORMAL.exitCode);
}
}
} catch (NoSuchElementException e) {
System.err.println("Not enough tokens on line " + lineno);
- die(1);
+ System.exit(ExitStatus.ABNORMAL.exitCode);
} catch (NumberFormatException e) {
System.err.println("First operand non-integer on line "
+ lineno);
- die(1);
+ System.exit(ExitStatus.ABNORMAL.exitCode);
}
codeMem.add(new Code(mnemonic, first, second, lineno));
line = br.readLine();
@@ -176,7 +196,7 @@ public PAL() {
* href="http://www.cs.adelaide.edu.au/users/third/cc/handouts/pal.pdf">The
* PAL Machine.
*/
- private void execute() {
+ ExitStatus execute() {
// Initialise program counter.
pc = 0;
@@ -217,7 +237,7 @@ private void execute() {
if (!(o instanceof Integer)) {
error(currInst, "Argument to INC must be an integer.");
- die(1);
+ return ExitStatus.ABNORMAL;
} else {
dataStack.incTop(((Integer) o).intValue());
}
@@ -227,7 +247,7 @@ private void execute() {
if (!(o instanceof Integer)) {
error(currInst, "Argument to JIF must be an integer.");
- die(1);
+ return ExitStatus.ABNORMAL;
}
tos = dataStack.pop();
@@ -235,7 +255,7 @@ private void execute() {
if (tos.getType() != Data.BOOL) {
dataStack.push(tos);
error(currInst, "JIF - top of stack not a boolean.");
- die(1);
+ return ExitStatus.ABNORMAL;
}
if (!((Boolean) tos.getValue()).booleanValue()) {
@@ -244,7 +264,7 @@ private void execute() {
if (destination < 1 || destination > codeMem.size()) {
dataStack.push(tos);
error(currInst, "JIF - attempt to jump outside code.");
- die(1);
+ return ExitStatus.ABNORMAL;
}
// Our code store uses zero-based indexing. For
@@ -258,19 +278,19 @@ private void execute() {
if (!(o instanceof Integer)) {
error(currInst, "Argument to JMP must be an integer.");
- die(1);
+ return ExitStatus.ABNORMAL;
}
int destination = ((Integer) o).intValue();
if (destination == 0) {
// "JMP 0 0" signifies program termination.
- die(0);
+ return ExitStatus.ABNORMAL;
}
if (destination < 1 || destination > codeMem.size()) {
error(currInst, "JMP - attempt to jump outside code.");
- die(1);
+ return ExitStatus.ABNORMAL;
}
// Our code store uses zero-based indexing. For
@@ -283,7 +303,7 @@ private void execute() {
if (!(o instanceof Integer)) {
error(currInst, "Argument to LCI must be an integer.");
- die(1);
+ return ExitStatus.ABNORMAL;
} else {
dataStack.push(new Data(Data.INT, o));
}
@@ -297,7 +317,7 @@ private void execute() {
if (!(o instanceof Float)) {
error(currInst, "Argument to LCR must be a real.");
- die(1);
+ return ExitStatus.ABNORMAL;
} else {
dataStack.push(new Data(Data.REAL, o));
}
@@ -307,13 +327,13 @@ private void execute() {
if (!(o instanceof String)) {
error(currInst, "Argument to LCS must be a string.");
- die(1);
+ return ExitStatus.ABNORMAL;
} else {
if (!(((String) o).startsWith("'") && (((String) o)
.endsWith("'")))) {
error(currInst,
"String must be delimited by single-quotes.");
- die(1);
+ return ExitStatus.ABNORMAL;
} else {
String oS = (String) o;
oS = oS.substring(1, oS.length() - 1);
@@ -327,7 +347,7 @@ private void execute() {
if (!(o instanceof Integer)) {
error(currInst, "Argument to LDA must be an integer.");
- die(1);
+ return ExitStatus.ABNORMAL;
}
int address = dataStack.getAddress(currInst.getFirst(),
@@ -344,7 +364,7 @@ private void execute() {
if (tos.getType() != Data.INT) {
dataStack.push(tos);
error(currInst, "LDI - top of stack must be an integer.");
- die(1);
+ return ExitStatus.ABNORMAL;
}
address = ((Integer) tos.getValue()).intValue();
@@ -360,7 +380,7 @@ private void execute() {
if (!(o instanceof Integer)) {
error(currInst, "Argument to LDV must be an integer");
- die(1);
+ return ExitStatus.ABNORMAL;
}
loadedVal = dataStack.get(currInst.getFirst(),
@@ -387,7 +407,10 @@ private void execute() {
break;
case Mnemonic.OPR:
- doOperation(currInst);
+ ExitStatus status = doOperation(currInst);
+ if (status == ExitStatus.ABNORMAL) {
+ return ExitStatus.ABNORMAL;
+ }
break;
case Mnemonic.RDI:
// Read an integer from stdin.
@@ -398,7 +421,10 @@ private void execute() {
if (intLine == null) {
// EOF reached.
currentException = reachedEOF;
- raiseException(currInst);
+ ExitStatus exceptionStatus = raiseException(currInst);
+ if (exceptionStatus == ExitStatus.ABNORMAL) {
+ return ExitStatus.ABNORMAL;
+ }
break;
}
int intVal = Integer.parseInt(intLine);
@@ -411,7 +437,10 @@ private void execute() {
System.err.println(e1);
} catch (NumberFormatException e2) {
currentException = typeMismatch;
- raiseException(currInst);
+ ExitStatus exceptionStatus = raiseException(currInst);
+ if (exceptionStatus == ExitStatus.ABNORMAL) {
+ return ExitStatus.ABNORMAL;
+ }
}
break;
case Mnemonic.RDR:
@@ -423,7 +452,10 @@ private void execute() {
if (realLine == null) {
// EOF reached.
currentException = reachedEOF;
- raiseException(currInst);
+ ExitStatus exceptionStatus = raiseException(currInst);
+ if (exceptionStatus == ExitStatus.ABNORMAL) {
+ return ExitStatus.ABNORMAL;
+ }
break;
}
float realVal = Float.parseFloat(realLine);
@@ -436,7 +468,10 @@ private void execute() {
System.err.println(e1);
} catch (NumberFormatException e2) {
currentException = typeMismatch;
- raiseException(currInst);
+ ExitStatus exceptionStatus = raiseException(currInst);
+ if (exceptionStatus == ExitStatus.ABNORMAL) {
+ return ExitStatus.ABNORMAL;
+ }
}
break;
case Mnemonic.REH:
@@ -445,7 +480,7 @@ private void execute() {
if (!(o instanceof Integer)) {
error(currInst, "Argument to REH must be an integer.");
- die(1);
+ return ExitStatus.ABNORMAL;
}
// Get the location of the exception handler pointer
@@ -463,7 +498,7 @@ private void execute() {
if (!(o instanceof Integer)) {
error(currInst, "Argument to SIG must be an integer.");
- die(1);
+ return ExitStatus.ABNORMAL;
}
int excType = ((Integer) o).intValue();
@@ -481,7 +516,10 @@ private void execute() {
}
// Raise the exception...
- raiseException(currInst);
+ ExitStatus exceptionStatus = raiseException(currInst);
+ if (exceptionStatus == ExitStatus.ABNORMAL) {
+ return ExitStatus.ABNORMAL;
+ }
break;
case Mnemonic.STI:
@@ -493,7 +531,7 @@ private void execute() {
if (tos.getType() != Data.INT) {
dataStack.push(tos);
error(currInst, "STI - top of stack must be an integer.");
- die(1);
+ return ExitStatus.ABNORMAL;
}
ntos = dataStack.pop();
@@ -510,7 +548,7 @@ private void execute() {
if (!(o instanceof Integer)) {
error(currInst, "Argument to STO must be an integer.");
- die(1);
+ return ExitStatus.ABNORMAL;
}
tos = dataStack.pop();
@@ -529,7 +567,7 @@ private void execute() {
System.err.println("Program failed to execute a termination"
+ " instruction (JMP 0 0).");
- die(1);
+ return ExitStatus.ABNORMAL;
}
/**
@@ -543,17 +581,17 @@ private void execute() {
* reaches here, that object contains an OPR
* mnemonic.
*/
- public void doOperation(Code currInst) {
+ public ExitStatus doOperation(Code currInst) {
Object o = currInst.getSecond();
int opr;
if (!(o instanceof Integer)) {
error(currInst, "Argument to OPR must be an integer.");
- die(1);
+ return ExitStatus.ABNORMAL;
}
opr = ((Integer) o).intValue();
if (opr < 0 || opr > 31) {
error(currInst, "Argument to OPR must be in range 0-31.");
- die(1);
+ return ExitStatus.ABNORMAL;
}
Data returnPoint, tos, ntos, dynamicLink;
@@ -620,7 +658,7 @@ public void doOperation(Code currInst) {
tos.setValue(new Float(-oldValue));
} else {
error(currInst, "Cannot negate boolean, string or UNDEF value.");
- die(1);
+ return ExitStatus.ABNORMAL;
}
break;
case 3:
@@ -638,7 +676,7 @@ public void doOperation(Code currInst) {
dataStack.push(tos);
error(currInst, "Values for arithmetic operations must be"
+ " of same type.");
- die(1);
+ return ExitStatus.ABNORMAL;
} else {
int type = tos.getType();
if (type != Data.INT && type != Data.REAL) {
@@ -646,7 +684,7 @@ public void doOperation(Code currInst) {
dataStack.push(tos);
error(currInst, "Values for arithmetic operations must be"
+ " of type integer or real.");
- die(1);
+ return ExitStatus.ABNORMAL;
}
if (type == Data.INT) {
int int1 = ((Integer) ntos.getValue()).intValue();
@@ -669,7 +707,7 @@ public void doOperation(Code currInst) {
dataStack.push(ntos);
dataStack.push(tos);
error(currInst, "Attempt to divide by zero.");
- die(1);
+ return ExitStatus.ABNORMAL;
}
dataStack.push(new Data(Data.INT, new Integer(int1
@@ -698,7 +736,7 @@ public void doOperation(Code currInst) {
dataStack.push(ntos);
dataStack.push(tos);
error(currInst, "Attempt to divide by zero.");
- die(1);
+ return ExitStatus.ABNORMAL;
}
dataStack.push(new Data(Data.REAL, new Float(flt1
@@ -715,7 +753,7 @@ public void doOperation(Code currInst) {
if (dataStack.peek().getType() != Data.INT) {
error(currInst, "Exponent must be of type integer.");
- die(1);
+ return ExitStatus.ABNORMAL;
}
tos = dataStack.pop();
int exponent = ((Integer) tos.getValue()).intValue();
@@ -723,7 +761,7 @@ public void doOperation(Code currInst) {
int baseType = dataStack.peek().getType();
if (baseType != Data.INT && baseType != Data.REAL) {
error(currInst, "Base must be of type integer or real.");
- die(1);
+ return ExitStatus.ABNORMAL;
}
ntos = dataStack.pop();
if (baseType == Data.INT) {
@@ -746,7 +784,7 @@ public void doOperation(Code currInst) {
dataStack.push(tos);
error(currInst,
"Both arguments to OPR 8 must be of type string.");
- die(1);
+ return ExitStatus.ABNORMAL;
}
String sResult = (String) ntos.getValue();
sResult += (String) tos.getValue();
@@ -757,7 +795,7 @@ public void doOperation(Code currInst) {
if (dataStack.peek().getType() != Data.INT) {
error(currInst, "Argument to OPR 9 must be of type integer.");
- die(1);
+ return ExitStatus.ABNORMAL;
} else {
tos = dataStack.pop();
// NB the % operator will give a negative for a
@@ -786,7 +824,7 @@ public void doOperation(Code currInst) {
dataStack.push(tos);
error(currInst, "Values for arithmetic operations must be"
+ " of same type.");
- die(1);
+ return ExitStatus.ABNORMAL;
} else {
int type = tos.getType();
if (type != Data.INT && type != Data.REAL) {
@@ -794,7 +832,7 @@ public void doOperation(Code currInst) {
dataStack.push(tos);
error(currInst, "Values for arithmetic operations must be"
+ " of type integer or real.");
- die(1);
+ return ExitStatus.ABNORMAL;
}
if (type == Data.INT) {
int int1 = ((Integer) ntos.getValue()).intValue();
@@ -867,7 +905,7 @@ public void doOperation(Code currInst) {
if (tos.getType() != Data.BOOL) {
dataStack.push(tos);
error(currInst, "Top of stack must be a boolean.");
- die(1);
+ return ExitStatus.ABNORMAL;
}
boolean bResult = !((Boolean) tos.getValue()).booleanValue();
@@ -905,7 +943,7 @@ public void doOperation(Code currInst) {
|| dataStack.peek().getType() == Data.UNDEF) {
error(currInst, "OPR 20 can only print values"
+ " of type integer, real or string.");
- die(1);
+ return ExitStatus.ABNORMAL;
} else {
tos = dataStack.pop();
System.out.print(tos);
@@ -941,7 +979,7 @@ public void doOperation(Code currInst) {
if (dataStack.peek().getType() != Data.INT) {
error(currInst, "Integer to real conversion can only be"
+ " performed on a value of type integer.");
- die(1);
+ return ExitStatus.ABNORMAL;
}
float fAns = ((Integer) dataStack.pop().getValue()).floatValue();
dataStack.push(new Data(Data.REAL, new Float(fAns)));
@@ -952,7 +990,7 @@ public void doOperation(Code currInst) {
if (dataStack.peek().getType() != Data.REAL) {
error(currInst, "Real to integer conversion can only be"
+ " performed on a value of type real.");
- die(1);
+ return ExitStatus.ABNORMAL;
}
int iResult = ((Float) dataStack.pop().getValue()).intValue();
dataStack.push(new Data(Data.INT, new Integer(iResult)));
@@ -963,7 +1001,7 @@ public void doOperation(Code currInst) {
if (dataStack.peek().getType() != Data.INT) {
error(currInst, "Integer to string conversion can only be"
+ " performed on a value of type integer.");
- die(1);
+ return ExitStatus.ABNORMAL;
}
dataStack.push(new Data(Data.STRING, dataStack.pop().getValue()
.toString()));
@@ -974,7 +1012,7 @@ public void doOperation(Code currInst) {
if (dataStack.peek().getType() != Data.REAL) {
error(currInst, "Real to string conversion can only be"
+ " performed on value of type real.");
- die(1);
+ return ExitStatus.ABNORMAL;
}
dataStack.push(new Data(Data.STRING, dataStack.pop().getValue()
.toString()));
@@ -989,7 +1027,7 @@ public void doOperation(Code currInst) {
dataStack.push(tos);
error(currInst, "Logical and can only be"
+ " performed on values of type boolean.");
- die(1);
+ return ExitStatus.ABNORMAL;
}
boolean bool1 = ((Boolean) tos.getValue()).booleanValue();
boolean bool2 = ((Boolean) ntos.getValue()).booleanValue();
@@ -1005,7 +1043,7 @@ public void doOperation(Code currInst) {
dataStack.push(tos);
error(currInst, "Logical or can only be"
+ " performed on values of type boolean.");
- die(1);
+ return ExitStatus.ABNORMAL;
}
bool1 = ((Boolean) tos.getValue()).booleanValue();
bool2 = ((Boolean) ntos.getValue()).booleanValue();
@@ -1020,7 +1058,7 @@ public void doOperation(Code currInst) {
dataStack.push(tos);
error(currInst, "OPR 0 31 expects an integer value"
+ "on top of the stack.");
- die(1);
+ return ExitStatus.ABNORMAL;
}
int testValue = ((Integer) tos.getValue()).intValue();
@@ -1031,7 +1069,7 @@ public void doOperation(Code currInst) {
default:
System.out.println("OPR " + opr + ": not implemented.");
}
- return;
+ return ExitStatus.NORMAL;
}
/**
@@ -1077,11 +1115,11 @@ private Object makeObject(String input) {
* The Code
object which caused the exception. Used
* to add information to error messages.
*/
- private void raiseException(Code currInst) {
+ private ExitStatus raiseException(Code currInst) {
// The Program Abort signal cannot be caught.
if (currentException == programAbort) {
error(currInst, "A Program Abort signal was raised.");
- die(1);
+ return ExitStatus.ABNORMAL;
}
Data handlerLocation, dynamicLink;
@@ -1094,14 +1132,14 @@ private void raiseException(Code currInst) {
if (handlerLocation.getType() != Data.INT) {
error(currInst, "Exception handler address must be an integer.");
- die(1);
+ return ExitStatus.ABNORMAL;
}
handlerAddress = ((Integer) handlerLocation.getValue()).intValue();
if (handlerAddress < 0 || handlerAddress > codeMem.size()) {
error(currInst, "Exception handler address out of code range.");
- die(1);
+ return ExitStatus.ABNORMAL;
}
if (handlerAddress == 0) {
@@ -1143,8 +1181,9 @@ private void raiseException(Code currInst) {
// No handler was found.
error(currInst, "Exception #" + currentException
+ " never handled!");
- die(1);
+ return ExitStatus.ABNORMAL;
}
+ return ExitStatus.NORMAL;
}
/**
@@ -1169,17 +1208,6 @@ private void error(Code currInst, String s) {
return;
}
- /**
- * Die due to an error.
- *
- * @param err
- * An arbitrary error code. This integer is returned to the
- * operating system via the System.exit
method.
- */
- private void die(int err) {
- System.exit(err);
- }
-
/**
* Simple usage information.
*/
diff --git a/src/test/java/net/logicsquad/pal/PALTest.java b/src/test/java/net/logicsquad/pal/PALTest.java
new file mode 100644
index 0000000..dc1894b
--- /dev/null
+++ b/src/test/java/net/logicsquad/pal/PALTest.java
@@ -0,0 +1,93 @@
+package net.logicsquad.pal;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.PrintStream;
+import java.nio.charset.StandardCharsets;
+import java.util.List;
+
+import org.junit.jupiter.api.Test;
+
+/**
+ * "Functional tests" on {@link PAL}. What we're doing here is just running the existing "test suite" using JUnit. We already have expected
+ * output for a set of input files. Due to {@link PAL}'s fondness for calling {@code System.exit()}, we can't run all of these just yet.
+ *
+ * @author paulh
+ */
+public class PALTest {
+ private static final List NON_INTERACTIVE_INPUTS = List.of(
+ "BASICS",
+ "BOOLS",
+ "INC",
+ "LOCALS",
+ "OPR-11",
+ "OPR-12",
+ "OPR-13",
+ "OPR-14",
+ "OPR-15",
+ "OPR-2",
+ "OPR-24",
+ "OPR-29-30",
+ "OPR-3-4-5-6",
+ "OPR-6a",
+ "OPR-6b",
+ "OPR-7",
+ "OPR-8",
+ "OPR-8-27-28",
+ "SIGa",
+ "SIGb",
+ "SIGc",
+ "SIGd",
+ "STRINGPARSE");
+
+ private static final List INTERACTIVE_INPUTS = List.of(
+ "FACTITER",
+ "FACTREC",
+ "LAB",
+ "OPR-19",
+ "OPR-22",
+ "OPR-23",
+ "SIGe",
+ "SIGf",
+ "SIGg",
+ "SIGh");
+
+ @Test
+ public void nonInteractiveTests() throws IOException {
+ for (String input : NON_INTERACTIVE_INPUTS) {
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ System.setOut(new PrintStream(baos));
+ System.setErr(new PrintStream(baos));
+ InputStream is = PALTest.class.getResourceAsStream("/basic/" + input);
+ PAL pal = new PAL(is);
+ pal.execute();
+ baos.flush();
+ String output = new String(baos.toByteArray());
+ String expected = new String(PALTest.class.getResourceAsStream("/basic/" + input + ".ref").readAllBytes(), StandardCharsets.UTF_8);
+ assertEquals(expected, output);
+ }
+ return;
+ }
+
+ @Test
+ public void interactiveTests() throws IOException {
+ for (String input : INTERACTIVE_INPUTS) {
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ System.setOut(new PrintStream(baos));
+ System.setErr(new PrintStream(baos));
+ InputStream response = PALTest.class.getResourceAsStream("/interactive/" + input + ".in");
+ System.setIn(response);
+ InputStream is = PALTest.class.getResourceAsStream("/interactive/" + input);
+ PAL pal = new PAL(is);
+ pal.execute();
+ baos.flush();
+ String output = new String(baos.toByteArray());
+ String expected = new String(PALTest.class.getResourceAsStream("/interactive/" + input + ".ref").readAllBytes(), StandardCharsets.UTF_8);
+ assertEquals(expected, output);
+ }
+ return;
+ }
+}
diff --git a/test/basic/BASICS b/src/test/resources/basic/BASICS
similarity index 100%
rename from test/basic/BASICS
rename to src/test/resources/basic/BASICS
diff --git a/test/basic/BASICS.ref b/src/test/resources/basic/BASICS.ref
similarity index 100%
rename from test/basic/BASICS.ref
rename to src/test/resources/basic/BASICS.ref
diff --git a/test/basic/BOOLS b/src/test/resources/basic/BOOLS
similarity index 100%
rename from test/basic/BOOLS
rename to src/test/resources/basic/BOOLS
diff --git a/test/basic/BOOLS.ref b/src/test/resources/basic/BOOLS.ref
similarity index 100%
rename from test/basic/BOOLS.ref
rename to src/test/resources/basic/BOOLS.ref
diff --git a/test/basic/INC b/src/test/resources/basic/INC
similarity index 100%
rename from test/basic/INC
rename to src/test/resources/basic/INC
diff --git a/test/basic/INC.ref b/src/test/resources/basic/INC.ref
similarity index 100%
rename from test/basic/INC.ref
rename to src/test/resources/basic/INC.ref
diff --git a/test/basic/LOCALS b/src/test/resources/basic/LOCALS
similarity index 100%
rename from test/basic/LOCALS
rename to src/test/resources/basic/LOCALS
diff --git a/test/basic/LOCALS.ref b/src/test/resources/basic/LOCALS.ref
similarity index 100%
rename from test/basic/LOCALS.ref
rename to src/test/resources/basic/LOCALS.ref
diff --git a/test/basic/OPR-11 b/src/test/resources/basic/OPR-11
similarity index 100%
rename from test/basic/OPR-11
rename to src/test/resources/basic/OPR-11
diff --git a/test/basic/OPR-11.ref b/src/test/resources/basic/OPR-11.ref
similarity index 100%
rename from test/basic/OPR-11.ref
rename to src/test/resources/basic/OPR-11.ref
diff --git a/test/basic/OPR-12 b/src/test/resources/basic/OPR-12
similarity index 100%
rename from test/basic/OPR-12
rename to src/test/resources/basic/OPR-12
diff --git a/test/basic/OPR-12.ref b/src/test/resources/basic/OPR-12.ref
similarity index 100%
rename from test/basic/OPR-12.ref
rename to src/test/resources/basic/OPR-12.ref
diff --git a/test/basic/OPR-13 b/src/test/resources/basic/OPR-13
similarity index 100%
rename from test/basic/OPR-13
rename to src/test/resources/basic/OPR-13
diff --git a/test/basic/OPR-13.ref b/src/test/resources/basic/OPR-13.ref
similarity index 100%
rename from test/basic/OPR-13.ref
rename to src/test/resources/basic/OPR-13.ref
diff --git a/test/basic/OPR-14 b/src/test/resources/basic/OPR-14
similarity index 100%
rename from test/basic/OPR-14
rename to src/test/resources/basic/OPR-14
diff --git a/test/basic/OPR-14.ref b/src/test/resources/basic/OPR-14.ref
similarity index 100%
rename from test/basic/OPR-14.ref
rename to src/test/resources/basic/OPR-14.ref
diff --git a/test/basic/OPR-15 b/src/test/resources/basic/OPR-15
similarity index 100%
rename from test/basic/OPR-15
rename to src/test/resources/basic/OPR-15
diff --git a/test/basic/OPR-15.ref b/src/test/resources/basic/OPR-15.ref
similarity index 100%
rename from test/basic/OPR-15.ref
rename to src/test/resources/basic/OPR-15.ref
diff --git a/test/basic/OPR-2 b/src/test/resources/basic/OPR-2
similarity index 100%
rename from test/basic/OPR-2
rename to src/test/resources/basic/OPR-2
diff --git a/test/basic/OPR-2.ref b/src/test/resources/basic/OPR-2.ref
similarity index 100%
rename from test/basic/OPR-2.ref
rename to src/test/resources/basic/OPR-2.ref
diff --git a/test/basic/OPR-24 b/src/test/resources/basic/OPR-24
similarity index 100%
rename from test/basic/OPR-24
rename to src/test/resources/basic/OPR-24
diff --git a/test/basic/OPR-24.ref b/src/test/resources/basic/OPR-24.ref
similarity index 51%
rename from test/basic/OPR-24.ref
rename to src/test/resources/basic/OPR-24.ref
index 983e76c..acdfe3c 100644
--- a/test/basic/OPR-24.ref
+++ b/src/test/resources/basic/OPR-24.ref
@@ -1,6 +1,6 @@
Runtime Error:
-test/basic/OPR-24:3:JIF - top of stack not a boolean.
+CODE:3:JIF - top of stack not a boolean.
JIF 0 42
Stack dump:
diff --git a/test/basic/OPR-29-30 b/src/test/resources/basic/OPR-29-30
similarity index 100%
rename from test/basic/OPR-29-30
rename to src/test/resources/basic/OPR-29-30
diff --git a/src/test/resources/basic/OPR-29-30.ref b/src/test/resources/basic/OPR-29-30.ref
new file mode 100644
index 0000000..8ea1b07
--- /dev/null
+++ b/src/test/resources/basic/OPR-29-30.ref
@@ -0,0 +1,12 @@
+
+Runtime Error:
+CODE:4:OPR 20 can only print values of type integer, real or string.
+OPR 0 20
+
+Stack dump:
+----------
+true
+0
+0
+0
+0
diff --git a/test/basic/OPR-3-4-5-6 b/src/test/resources/basic/OPR-3-4-5-6
similarity index 100%
rename from test/basic/OPR-3-4-5-6
rename to src/test/resources/basic/OPR-3-4-5-6
diff --git a/test/basic/OPR-3-4-5-6.ref b/src/test/resources/basic/OPR-3-4-5-6.ref
similarity index 100%
rename from test/basic/OPR-3-4-5-6.ref
rename to src/test/resources/basic/OPR-3-4-5-6.ref
diff --git a/test/basic/OPR-6a b/src/test/resources/basic/OPR-6a
similarity index 100%
rename from test/basic/OPR-6a
rename to src/test/resources/basic/OPR-6a
diff --git a/test/basic/OPR-6a.ref b/src/test/resources/basic/OPR-6a.ref
similarity index 56%
rename from test/basic/OPR-6a.ref
rename to src/test/resources/basic/OPR-6a.ref
index 6bd76e4..6120aa4 100644
--- a/test/basic/OPR-6a.ref
+++ b/src/test/resources/basic/OPR-6a.ref
@@ -1,6 +1,6 @@
Runtime Error:
-test/basic/OPR-6a:3:Attempt to divide by zero.
+CODE:3:Attempt to divide by zero.
OPR 0 6
Stack dump:
diff --git a/test/basic/OPR-6b b/src/test/resources/basic/OPR-6b
similarity index 100%
rename from test/basic/OPR-6b
rename to src/test/resources/basic/OPR-6b
diff --git a/test/basic/OPR-6b.ref b/src/test/resources/basic/OPR-6b.ref
similarity index 57%
rename from test/basic/OPR-6b.ref
rename to src/test/resources/basic/OPR-6b.ref
index a6a4161..855dd73 100644
--- a/test/basic/OPR-6b.ref
+++ b/src/test/resources/basic/OPR-6b.ref
@@ -1,6 +1,6 @@
Runtime Error:
-test/basic/OPR-6b:5:Attempt to divide by zero.
+CODE:5:Attempt to divide by zero.
OPR 0 6
Stack dump:
diff --git a/test/basic/OPR-7 b/src/test/resources/basic/OPR-7
similarity index 100%
rename from test/basic/OPR-7
rename to src/test/resources/basic/OPR-7
diff --git a/test/basic/OPR-7.ref b/src/test/resources/basic/OPR-7.ref
similarity index 55%
rename from test/basic/OPR-7.ref
rename to src/test/resources/basic/OPR-7.ref
index 4a04d16..bbd93f4 100644
--- a/test/basic/OPR-7.ref
+++ b/src/test/resources/basic/OPR-7.ref
@@ -1,6 +1,6 @@
86.25
Runtime Error:
-test/basic/OPR-7:11:Exponent must be of type integer.
+CODE:11:Exponent must be of type integer.
OPR 0 7
Stack dump:
diff --git a/test/basic/OPR-8 b/src/test/resources/basic/OPR-8
similarity index 100%
rename from test/basic/OPR-8
rename to src/test/resources/basic/OPR-8
diff --git a/test/basic/OPR-8-27-28 b/src/test/resources/basic/OPR-8-27-28
similarity index 100%
rename from test/basic/OPR-8-27-28
rename to src/test/resources/basic/OPR-8-27-28
diff --git a/test/basic/OPR-8-27-28.ref b/src/test/resources/basic/OPR-8-27-28.ref
similarity index 100%
rename from test/basic/OPR-8-27-28.ref
rename to src/test/resources/basic/OPR-8-27-28.ref
diff --git a/test/basic/OPR-8.ref b/src/test/resources/basic/OPR-8.ref
similarity index 100%
rename from test/basic/OPR-8.ref
rename to src/test/resources/basic/OPR-8.ref
diff --git a/test/basic/SIGa b/src/test/resources/basic/SIGa
similarity index 100%
rename from test/basic/SIGa
rename to src/test/resources/basic/SIGa
diff --git a/test/basic/SIGa.ref b/src/test/resources/basic/SIGa.ref
similarity index 54%
rename from test/basic/SIGa.ref
rename to src/test/resources/basic/SIGa.ref
index 69ac777..ca31af7 100644
--- a/test/basic/SIGa.ref
+++ b/src/test/resources/basic/SIGa.ref
@@ -1,6 +1,6 @@
Runtime Error:
-test/basic/SIGa:1:Exception #0 never handled!
+CODE:1:Exception #0 never handled!
SIG 0 0
Stack dump:
diff --git a/test/basic/SIGb b/src/test/resources/basic/SIGb
similarity index 100%
rename from test/basic/SIGb
rename to src/test/resources/basic/SIGb
diff --git a/test/basic/SIGb.ref b/src/test/resources/basic/SIGb.ref
similarity index 54%
rename from test/basic/SIGb.ref
rename to src/test/resources/basic/SIGb.ref
index 40a78b0..59aa070 100644
--- a/test/basic/SIGb.ref
+++ b/src/test/resources/basic/SIGb.ref
@@ -1,6 +1,6 @@
Runtime Error:
-test/basic/SIGb:2:Exception #0 never handled!
+CODE:2:Exception #0 never handled!
SIG 0 0
Stack dump:
diff --git a/test/basic/SIGc b/src/test/resources/basic/SIGc
similarity index 100%
rename from test/basic/SIGc
rename to src/test/resources/basic/SIGc
diff --git a/test/basic/SIGc.ref b/src/test/resources/basic/SIGc.ref
similarity index 51%
rename from test/basic/SIGc.ref
rename to src/test/resources/basic/SIGc.ref
index 4115614..0f83722 100644
--- a/test/basic/SIGc.ref
+++ b/src/test/resources/basic/SIGc.ref
@@ -1,6 +1,6 @@
Runtime Error:
-test/basic/SIGc:2:A Program Abort signal was raised.
+CODE:2:A Program Abort signal was raised.
SIG 0 1
Stack dump:
diff --git a/test/basic/SIGd b/src/test/resources/basic/SIGd
similarity index 100%
rename from test/basic/SIGd
rename to src/test/resources/basic/SIGd
diff --git a/test/basic/SIGd.ref b/src/test/resources/basic/SIGd.ref
similarity index 100%
rename from test/basic/SIGd.ref
rename to src/test/resources/basic/SIGd.ref
diff --git a/test/basic/STRINGPARSE b/src/test/resources/basic/STRINGPARSE
similarity index 100%
rename from test/basic/STRINGPARSE
rename to src/test/resources/basic/STRINGPARSE
diff --git a/test/basic/STRINGPARSE.ref b/src/test/resources/basic/STRINGPARSE.ref
similarity index 100%
rename from test/basic/STRINGPARSE.ref
rename to src/test/resources/basic/STRINGPARSE.ref
diff --git a/test/interactive/FACTITER b/src/test/resources/interactive/FACTITER
similarity index 100%
rename from test/interactive/FACTITER
rename to src/test/resources/interactive/FACTITER
diff --git a/test/interactive/FACTITER.in b/src/test/resources/interactive/FACTITER.in
similarity index 100%
rename from test/interactive/FACTITER.in
rename to src/test/resources/interactive/FACTITER.in
diff --git a/test/interactive/FACTITER.ref b/src/test/resources/interactive/FACTITER.ref
similarity index 100%
rename from test/interactive/FACTITER.ref
rename to src/test/resources/interactive/FACTITER.ref
diff --git a/test/interactive/FACTREC b/src/test/resources/interactive/FACTREC
similarity index 100%
rename from test/interactive/FACTREC
rename to src/test/resources/interactive/FACTREC
diff --git a/test/interactive/FACTREC.in b/src/test/resources/interactive/FACTREC.in
similarity index 100%
rename from test/interactive/FACTREC.in
rename to src/test/resources/interactive/FACTREC.in
diff --git a/test/interactive/FACTREC.ref b/src/test/resources/interactive/FACTREC.ref
similarity index 100%
rename from test/interactive/FACTREC.ref
rename to src/test/resources/interactive/FACTREC.ref
diff --git a/test/interactive/LAB b/src/test/resources/interactive/LAB
similarity index 100%
rename from test/interactive/LAB
rename to src/test/resources/interactive/LAB
diff --git a/test/interactive/LAB.in b/src/test/resources/interactive/LAB.in
similarity index 100%
rename from test/interactive/LAB.in
rename to src/test/resources/interactive/LAB.in
diff --git a/test/interactive/LAB.ref b/src/test/resources/interactive/LAB.ref
similarity index 100%
rename from test/interactive/LAB.ref
rename to src/test/resources/interactive/LAB.ref
diff --git a/test/interactive/OPR-19 b/src/test/resources/interactive/OPR-19
similarity index 100%
rename from test/interactive/OPR-19
rename to src/test/resources/interactive/OPR-19
diff --git a/test/interactive/OPR-19.in b/src/test/resources/interactive/OPR-19.in
similarity index 100%
rename from test/interactive/OPR-19.in
rename to src/test/resources/interactive/OPR-19.in
diff --git a/test/interactive/OPR-19.ref b/src/test/resources/interactive/OPR-19.ref
similarity index 100%
rename from test/interactive/OPR-19.ref
rename to src/test/resources/interactive/OPR-19.ref
diff --git a/test/interactive/OPR-22 b/src/test/resources/interactive/OPR-22
similarity index 100%
rename from test/interactive/OPR-22
rename to src/test/resources/interactive/OPR-22
diff --git a/test/interactive/OPR-22.in b/src/test/resources/interactive/OPR-22.in
similarity index 100%
rename from test/interactive/OPR-22.in
rename to src/test/resources/interactive/OPR-22.in
diff --git a/test/interactive/OPR-22.ref b/src/test/resources/interactive/OPR-22.ref
similarity index 100%
rename from test/interactive/OPR-22.ref
rename to src/test/resources/interactive/OPR-22.ref
diff --git a/test/interactive/OPR-23 b/src/test/resources/interactive/OPR-23
similarity index 100%
rename from test/interactive/OPR-23
rename to src/test/resources/interactive/OPR-23
diff --git a/test/interactive/OPR-23.in b/src/test/resources/interactive/OPR-23.in
similarity index 100%
rename from test/interactive/OPR-23.in
rename to src/test/resources/interactive/OPR-23.in
diff --git a/test/interactive/OPR-23.ref b/src/test/resources/interactive/OPR-23.ref
similarity index 100%
rename from test/interactive/OPR-23.ref
rename to src/test/resources/interactive/OPR-23.ref
diff --git a/test/interactive/SIGe b/src/test/resources/interactive/SIGe
similarity index 100%
rename from test/interactive/SIGe
rename to src/test/resources/interactive/SIGe
diff --git a/test/interactive/SIGe.in b/src/test/resources/interactive/SIGe.in
similarity index 100%
rename from test/interactive/SIGe.in
rename to src/test/resources/interactive/SIGe.in
diff --git a/test/interactive/SIGe.ref b/src/test/resources/interactive/SIGe.ref
similarity index 74%
rename from test/interactive/SIGe.ref
rename to src/test/resources/interactive/SIGe.ref
index f3ff4a0..6bace3d 100644
--- a/test/interactive/SIGe.ref
+++ b/src/test/resources/interactive/SIGe.ref
@@ -4,7 +4,7 @@ pTwo exh: exception 0.
Main exh: exception 0.
Runtime Error:
-test/interactive/SIGe:146:Exception #0 never handled!
+CODE:146:Exception #0 never handled!
SIG 0 0
Stack dump:
diff --git a/test/interactive/SIGf b/src/test/resources/interactive/SIGf
similarity index 100%
rename from test/interactive/SIGf
rename to src/test/resources/interactive/SIGf
diff --git a/test/interactive/SIGf.in b/src/test/resources/interactive/SIGf.in
similarity index 100%
rename from test/interactive/SIGf.in
rename to src/test/resources/interactive/SIGf.in
diff --git a/test/interactive/SIGf.ref b/src/test/resources/interactive/SIGf.ref
similarity index 71%
rename from test/interactive/SIGf.ref
rename to src/test/resources/interactive/SIGf.ref
index fbbceea..ea7f5e6 100644
--- a/test/interactive/SIGf.ref
+++ b/src/test/resources/interactive/SIGf.ref
@@ -1,7 +1,7 @@
Enter the exception type.
Runtime Error:
-test/interactive/SIGf:18:A Program Abort signal was raised.
+CODE:18:A Program Abort signal was raised.
SIG 0 1
Stack dump:
diff --git a/test/interactive/SIGg b/src/test/resources/interactive/SIGg
similarity index 100%
rename from test/interactive/SIGg
rename to src/test/resources/interactive/SIGg
diff --git a/test/interactive/SIGg.in b/src/test/resources/interactive/SIGg.in
similarity index 100%
rename from test/interactive/SIGg.in
rename to src/test/resources/interactive/SIGg.in
diff --git a/test/interactive/SIGg.ref b/src/test/resources/interactive/SIGg.ref
similarity index 79%
rename from test/interactive/SIGg.ref
rename to src/test/resources/interactive/SIGg.ref
index 4f23ead..25cdea2 100644
--- a/test/interactive/SIGg.ref
+++ b/src/test/resources/interactive/SIGg.ref
@@ -6,7 +6,7 @@ pTwo exh: exception 8.
Main exh: exception 8.
Runtime Error:
-test/interactive/SIGg:167:Exception #8 never handled!
+CODE:167:Exception #8 never handled!
SIG 0 0
Stack dump:
diff --git a/test/interactive/SIGh b/src/test/resources/interactive/SIGh
similarity index 100%
rename from test/interactive/SIGh
rename to src/test/resources/interactive/SIGh
diff --git a/test/interactive/SIGh.in b/src/test/resources/interactive/SIGh.in
similarity index 100%
rename from test/interactive/SIGh.in
rename to src/test/resources/interactive/SIGh.in
diff --git a/test/interactive/SIGh.ref b/src/test/resources/interactive/SIGh.ref
similarity index 54%
rename from test/interactive/SIGh.ref
rename to src/test/resources/interactive/SIGh.ref
index f08e622..f3a1816 100644
--- a/test/interactive/SIGh.ref
+++ b/src/test/resources/interactive/SIGh.ref
@@ -1,6 +1,6 @@
Runtime Error:
-test/interactive/SIGh:4:Exception #4 never handled!
+CODE:4:Exception #4 never handled!
SIG 0 0
Stack dump:
diff --git a/test/basic/OPR-29-30.ref b/test/basic/OPR-29-30.ref
deleted file mode 100644
index 1cc528c..0000000
--- a/test/basic/OPR-29-30.ref
+++ /dev/null
@@ -1,12 +0,0 @@
-
-Runtime Error:
-test/basic/OPR-29-30:4:OPR 20 can only print values of type integer, real or string.
-OPR 0 20
-
-Stack dump:
-----------
-true
-0
-0
-0
-0