Test conf

This commit is contained in:
Jan-Niclas Loosen
2025-10-25 22:40:18 +02:00
parent 81e1f84fd9
commit dbac3442e9
9 changed files with 353 additions and 229 deletions

View File

@@ -0,0 +1,15 @@
**Author: Jan-Niclas Loosen (1540907)**
The jar ``jar/tram.jar`` contains all required dependencies.
```bash
java -jar tram.jar <tramFile> [options]
```
**Optional arguments:**
`--debug`: Enables debug logging to the console.
`--debug <logFile>`: Enables debug logging and writes output to the specified file.
`--stack [v1,v2,...]`: Initializes the stack with the given integers (for example `[4,10]`).

View File

@@ -0,0 +1,39 @@
#!/usr/bin/env bash
set -e
OUT=jar
MAIN_CLASS=de.unitrier.st.uap.w25.tram.Main
JAR_NAME=tram.jar
LIB=lib
TMP=$OUT/tmp
# Clean and prepare
rm -rf "$OUT"
mkdir -p "$TMP"
# 1. Compile sources
if [ -d "$LIB" ]; then
javac -d "$TMP" -cp "$LIB/*" $(find src -name "*.java")
else
javac -d "$TMP" $(find src -name "*.java")
fi
# 2. Unpack dependency jars
if [ -d "$LIB" ]; then
for jar in "$LIB"/*.jar; do
[ -f "$jar" ] && jar xf "$jar" -C "$TMP"
done
fi
# 3. Create manifest
echo "Main-Class: $MAIN_CLASS" > "$TMP/MANIFEST.MF"
# 4. Package into single jar
jar cfm "$OUT/$JAR_NAME" "$TMP/MANIFEST.MF" -C "$TMP" .
# 5. Clean temporary files
rm -rf "$TMP"
echo "Built: $OUT/$JAR_NAME"
echo "Run with:"
echo " java -jar $OUT/$JAR_NAME"

Binary file not shown.

View File

@@ -26,7 +26,7 @@ public class AbstractMachine {
// Executes until halts and returns final configuration // Executes until halts and returns final configuration
public void execute() { public void execute() {
while(!isHalted()) { while (!isHalted()) {
executeStep(); executeStep();
} }
confStr(); confStr();
@@ -34,6 +34,7 @@ public class AbstractMachine {
// Executes single step and returns configuration // Executes single step and returns configuration
public void executeStep() { public void executeStep() {
try {
ensureCapacity(this.TOP); ensureCapacity(this.TOP);
Instruction inst = this.prog.get(this.PC); Instruction inst = this.prog.get(this.PC);
@@ -135,8 +136,7 @@ public class AbstractMachine {
// Pg. 13 // Pg. 13
if (this.stack.get(this.TOP) == 0) { if (this.stack.get(this.TOP) == 0) {
this.PC = arg1; this.PC = arg1;
} } else {
else {
this.PC += 1; this.PC += 1;
} }
this.TOP -= 1; this.TOP -= 1;
@@ -179,7 +179,15 @@ public class AbstractMachine {
this.TOP -= 1; this.TOP -= 1;
this.PC += 1; this.PC += 1;
break; break;
default: break; default:
break;
}
}
catch(Exception e) {
String line = this.prog.get(this.PC).toString();
String msg = e.getMessage();
msg = String.format("Error at PC=%d (%s): %s%nCurrent stack=%s", this.PC, line, msg, stackStr());
throw new RuntimeException(msg, e);
} }
} }
@@ -207,14 +215,13 @@ public class AbstractMachine {
protected void ensureCapacity(int currSize) { protected void ensureCapacity(int currSize) {
int size = stack.size(); int size = stack.size();
// Initialize stack with default capacity of 16 // Always ensure a minimum working capacity of 16
if (size == 0) { if (size < 16) {
for (int i = 0; i < 16; i++) stack.add(0); for (int i = size; i < 16; i++) stack.add(0);
return; size = 16;
} }
// Double list capacity when fewer than 8 free slots remain // Double list capacity when fewer than 8 free slots remain
// (largest instruction, INVOKE, needs 5 additional cells)
if (currSize >= size - 8) { if (currSize >= size - 8) {
int newSize = size * 2; int newSize = size * 2;
for (int i = size; i < newSize; i++) stack.add(0); for (int i = size; i < newSize; i++) stack.add(0);

View File

@@ -1,6 +1,9 @@
package de.unitrier.st.uap.w25.tram; package de.unitrier.st.uap.w25.tram;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import java.util.ArrayList;
import static org.junit.jupiter.api.Assertions.*; import static org.junit.jupiter.api.Assertions.*;
public class AbstractMachineTests { public class AbstractMachineTests {
@@ -66,8 +69,98 @@ public class AbstractMachineTests {
AbstractMachine tram = new AbstractMachine(code, new Integer[]{}); // empty stack AbstractMachine tram = new AbstractMachine(code, new Integer[]{}); // empty stack
tram.execute(); tram.execute();
assertTrue(tram.isHalted(), "Machine should halt after executing wrapper(4,10)."); assertTrue(tram.isHalted(), "Machine should halt.");
int result = tram.result(); int result = tram.result();
assertEquals(4, result, "Expected result of wrapper(4,10) = 4."); assertEquals(4, result, "Expected result of wrapper(4,10) = 4.");
} }
@Test
void testGGTx48y18() {
Instruction[] code = Assembler.readTRAMCode("tramcode/ggt.tram");
AbstractMachine tram = new AbstractMachine(code, new Integer[]{48, 18});
tram.execute();
assertTrue(tram.isHalted(), "Machine should halt.");
int result = tram.result();
assertEquals(6, result, "Expected ggt(48,18) = 6.");
}
@Test
void testGGTx270y192() {
Instruction[] code = Assembler.readTRAMCode("tramcode/ggt.tram");
AbstractMachine tram = new AbstractMachine(code, new Integer[]{270, 192});
tram.execute();
assertTrue(tram.isHalted(), "Machine should halt.");
int result = tram.result();
assertEquals(6, result, "Expected ggt(270,192) = 6.");
}
@Test
void testModPositive() {
Instruction[] code = Assembler.readTRAMCode("tramcode/mod.tram");
AbstractMachine tram = new AbstractMachine(code, new Integer[]{17, 5});
tram.execute();
assertTrue(tram.isHalted(), "Machine should halt.");
int result = tram.result();
assertEquals(2, result, "Expected 17 mod 5 = 2.");
}
@Test
void testModExactDivision() {
Instruction[] code = Assembler.readTRAMCode("tramcode/mod.tram");
AbstractMachine tram = new AbstractMachine(code, new Integer[]{20, 5});
tram.execute();
assertTrue(tram.isHalted(), "Machine should halt.");
int result = tram.result();
assertEquals(0, result, "Expected 20 mod 5 = 0.");
}
@Test
void testModSmallerDividend() {
Instruction[] code = Assembler.readTRAMCode("tramcode/mod.tram");
AbstractMachine tram = new AbstractMachine(code, new Integer[]{3, 10});
tram.execute();
assertTrue(tram.isHalted(), "Machine should halt.");
int result = tram.result();
assertEquals(3, result, "Expected 3 mod 10 = 3.");
}
@Test
void testGGTx17y5() {
Instruction[] code = Assembler.readTRAMCode("tramcode/ggt.tram");
AbstractMachine tram = new AbstractMachine(code, new Integer[]{17, 5});
tram.execute();
assertTrue(tram.isHalted(), "Machine should halt.");
int result = tram.result();
assertEquals(1, result, "Expected ggt(17,5) = 1.");
}
@Test
void testGGTx25y0() {
Instruction[] code = Assembler.readTRAMCode("tramcode/ggt.tram");
AbstractMachine tram = new AbstractMachine(code, new Integer[]{25, 0});
tram.execute();
assertTrue(tram.isHalted(), "Machine should halt.");
int result = tram.result();
assertEquals(25, result, "Expected ggt(25,0) = 25.");
}
@Test
void testGGTx0y25() {
Instruction[] code = Assembler.readTRAMCode("tramcode/ggt.tram");
AbstractMachine tram = new AbstractMachine(code, new Integer[]{0, 25});
tram.execute();
assertTrue(tram.isHalted(), "Machine should halt.");
int result = tram.result();
assertEquals(25, result, "Expected ggt(0,25) = 25.");
}
} }

View File

@@ -2,53 +2,19 @@ package de.unitrier.st.uap.w25.tram;
import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.Level;
import org.apache.logging.log4j.core.LoggerContext;
import org.apache.logging.log4j.core.config.builder.api.ConfigurationBuilder;
import org.apache.logging.log4j.core.config.builder.api.ConfigurationBuilderFactory;
import org.apache.logging.log4j.core.config.builder.impl.BuiltConfiguration;
import org.apache.logging.log4j.core.config.builder.api.AppenderComponentBuilder;
// Write subclass to enhance Single-Responsibility-Principle
public class LoggedMachine extends AbstractMachine { public class LoggedMachine extends AbstractMachine {
protected static Logger logger = LogManager.getLogger(LoggedMachine.class); private static final Logger logger = LogManager.getLogger(LoggedMachine.class);
protected int stepCounter = 0; private int stepCounter = 0;
// No changes in the constructor public LoggedMachine(Instruction[] prog) { super(prog); }
public LoggedMachine(Instruction[] prog) { public LoggedMachine(Instruction[] prog, Integer[] stack) { super(prog, stack); }
super(prog);
}
// No changes in the constructor
public LoggedMachine(Instruction[] prog, Integer[] stack) {
super(prog, stack);
}
// Enable file logging
public static void toFile(String fileName) {
ConfigurationBuilder<BuiltConfiguration> builder = ConfigurationBuilderFactory.newConfigurationBuilder();
AppenderComponentBuilder fileAppender = builder.newAppender("File", "File")
.addAttribute("fileName", fileName)
.add(builder.newLayout("PatternLayout")
.addAttribute("pattern", "%d{HH:mm:ss} %-5level %msg%n"));
builder.add(fileAppender);
builder.add(builder.newRootLogger(Level.DEBUG).add(builder.newAppenderRef("File")));
LoggerContext ctx = LoggerContext.getContext(false);
ctx.start(builder.build());
logger = LogManager.getLogger(LoggedMachine.class);
}
@Override @Override
public void execute() { public void execute() {
logger.info("Executing..."); logger.info("Executing...");
logger.debug("{}: {}", 0, confStr()); logger.debug("0: {}", confStr());
while(!isHalted()) { while (!isHalted()) executeStep();
executeStep();
}
logger.info("Finished with STACK: {}", stackStr()); logger.info("Finished with STACK: {}", stackStr());
} }

View File

@@ -2,8 +2,6 @@ package de.unitrier.st.uap.w25.tram;
import org.apache.logging.log4j.Level; import org.apache.logging.log4j.Level;
import org.apache.logging.log4j.core.config.Configurator; import org.apache.logging.log4j.core.config.Configurator;
import java.io.IOException;
import java.nio.file.Files; import java.nio.file.Files;
import java.nio.file.Path; import java.nio.file.Path;
import java.util.ArrayList; import java.util.ArrayList;
@@ -12,27 +10,21 @@ import java.util.List;
public final class Main { public final class Main {
private Main() {} private Main() {}
public static void main(String[] argv) { static void main(String[] argv) {
if (argv.length < 1) { if (argv.length < 1) {
System.err.println("Usage:"); System.err.println("Usage:");
System.err.println(" java -jar tram.jar <tramFile> [--debug [logFile]] [--stack [v1,v2,...]]"); System.err.println(" java -jar tram.jar <tramFile> [--debug] [--stack [v1,v2,...]]");
System.exit(1); System.exit(1);
} }
String tramPath = argv[0]; String tramPath = argv[0];
boolean debug = false; boolean debug = false;
String logFile = null;
String stackArg = null; String stackArg = null;
// Parse arguments // Parse arguments
for (int i = 1; i < argv.length; i++) { for (int i = 1; i < argv.length; i++) {
switch (argv[i]) { switch (argv[i]) {
case "--debug" -> { case "--debug" -> debug = true;
debug = true;
if (i + 1 < argv.length && !argv[i + 1].startsWith("--")) {
logFile = argv[++i];
}
}
case "--stack" -> { case "--stack" -> {
if (i + 1 < argv.length) { if (i + 1 < argv.length) {
stackArg = argv[++i]; stackArg = argv[++i];
@@ -55,24 +47,9 @@ public final class Main {
System.exit(4); System.exit(4);
} }
// Configure logging // Configure console logging only
if (debug) { if (debug) {
Configurator.setRootLevel(Level.DEBUG); Configurator.setRootLevel(Level.DEBUG);
if (logFile != null) {
Path logPath = Path.of(logFile);
try {
Files.createDirectories(logPath.getParent() != null ? logPath.getParent() : Path.of("."));
if (!Files.exists(logPath)) Files.createFile(logPath);
} catch (IOException e) {
System.err.println("Error: cannot create log file -> " + logPath);
e.printStackTrace();
System.exit(5);
}
LoggedMachine.toFile(logPath.toString());
System.out.println("Debug logging to file: " + logPath);
} else {
System.out.println("Debug logging to console.");
}
} else { } else {
Configurator.setRootLevel(Level.INFO); Configurator.setRootLevel(Level.INFO);
} }
@@ -88,20 +65,13 @@ public final class Main {
System.out.println("Execution complete."); System.out.println("Execution complete.");
} }
/** // Parse "[1,2,3]" or "1,2,3"
* Parses a stack argument given in Python list notation, e.g. "[1,2,3]"
*/
private static Integer[] parseStack(String stackArg) { private static Integer[] parseStack(String stackArg) {
if (stackArg == null || stackArg.isBlank()) { if (stackArg == null || stackArg.isBlank()) return new Integer[]{};
return new Integer[] {};
}
String trimmed = stackArg.trim(); String trimmed = stackArg.trim();
if (trimmed.startsWith("[") && trimmed.endsWith("]")) { if (trimmed.startsWith("[") && trimmed.endsWith("]"))
trimmed = trimmed.substring(1, trimmed.length() - 1); trimmed = trimmed.substring(1, trimmed.length() - 1);
} if (trimmed.isBlank()) return new Integer[]{};
if (trimmed.isBlank()) {
return new Integer[] {};
}
String[] parts = trimmed.split(","); String[] parts = trimmed.split(",");
List<Integer> list = new ArrayList<>(); List<Integer> list = new ArrayList<>();

View File

@@ -0,0 +1,22 @@
// Quellkode: ggt(a,b) { if (b==0) then a else ggt(b, a mod b) }
// Annahmen: Die Argumente a und b werden durch Kellerzellen 0 und 1 repräsentiert,
// sowie PP=0, FP=0 und TOP=-1
INVOKE 2 LGGT 0
POP
HALT
LGGT:
// b
LOAD 1 0
// if b != 0 -> 1
IFZERO 1
// return a
LOAD 0 0
RETURN
L1:
LOAD 1 0 // b
LOAD 0 0
LOAD 1 0
MOD // a mod b
INVOKE 2 LGGT 0 // ggt(b, a mod b)
RETURN

View File

@@ -0,0 +1,12 @@
# Quellkode: let mod(a,b) { a - (a / b) * b } in mod(14,5)
# Annahmen: a→stack[0], b→stack[1], PP=0, FP=0, TOP=-1
INVOKE 2 MOD 0
HALT
MOD: LOAD 1 0
LOAD 1 0
DIV
LOAD 2 0
MUL
SUB
RETURN