mirror of
https://github.com/simon987/Much-Assembly-Required.git
synced 2025-04-10 14:26:45 +00:00
Merge ef1b9ee9a81c555a19247e26a1eb526e4b272105 into 18a6858eddfd410e38b7692c7b7e95f9148bf780
This commit is contained in:
commit
fbce0341b4
@ -55,14 +55,17 @@ public class CubotInventory extends HardwareModule {
|
||||
private void scanItem() {
|
||||
int x = getCpu().getRegisterSet().getRegister("X").getValue();
|
||||
Item item = inventory.get(position);
|
||||
item.digitize(unit.getCpu().getMemory(), x);
|
||||
if (item != null) {
|
||||
item.digitize(unit.getCpu().getMemory(), x);
|
||||
}
|
||||
}
|
||||
|
||||
public Item clearItem() {
|
||||
Item item = inventory.get(position);
|
||||
item.clear(unit);
|
||||
inventory.remove(position);
|
||||
|
||||
if (item != null) {
|
||||
item.clear(unit);
|
||||
inventory.remove(position);
|
||||
}
|
||||
return item;
|
||||
}
|
||||
|
||||
|
@ -79,7 +79,7 @@ public class Assembler {
|
||||
String[] tokens = line.trim().split("\\s+");
|
||||
String mnemonic = tokens[0];
|
||||
|
||||
if (mnemonic.toUpperCase().equals("ORG")) {
|
||||
if (mnemonic.equalsIgnoreCase("ORG")) {
|
||||
if (tokens.length > 1) {
|
||||
try {
|
||||
result.origin = (Integer.decode(tokens[1]));
|
||||
@ -124,6 +124,9 @@ public class Assembler {
|
||||
return line.replaceAll("\\s+", "").isEmpty();
|
||||
}
|
||||
|
||||
|
||||
private static final Pattern DUP_PATTERN = Pattern.compile("(.+)\\s+DUP(.+)");
|
||||
private static final Pattern STRING_PATTERN = Pattern.compile("\"(.*)\"$");
|
||||
/**
|
||||
* Parse the DW instruction (Define word). Handles DUP operator
|
||||
*
|
||||
@ -132,7 +135,7 @@ public class Assembler {
|
||||
* @param labels Map of labels
|
||||
* @return Encoded instruction, null if the line is not a DW instruction
|
||||
*/
|
||||
private static byte[] parseDWInstruction(String line, HashMap<String, Character> labels, int currentLine)
|
||||
private byte[] parseDWInstruction(String line, HashMap<String, Character> labels, int currentLine)
|
||||
throws InvalidOperandException {
|
||||
|
||||
ByteArrayOutputStream bos = new ByteArrayOutputStream();
|
||||
@ -140,7 +143,7 @@ public class Assembler {
|
||||
|
||||
//System.out.println(line);
|
||||
|
||||
if (line.length() >= 2 && line.substring(0, 2).toUpperCase().equals("DW")) {
|
||||
if (line.length() >= 2 && line.substring(0, 2).equalsIgnoreCase("DW")) {
|
||||
|
||||
try {
|
||||
|
||||
@ -150,18 +153,10 @@ public class Assembler {
|
||||
for (String value : values) {
|
||||
|
||||
value = value.trim();
|
||||
|
||||
String[] valueTokens = value.split("\\s+");
|
||||
|
||||
//Handle DUP operator
|
||||
if (valueTokens.length == 2 && valueTokens[1].toUpperCase().contains("DUP(")) {
|
||||
out.write(parseDUPOperator16(valueTokens, labels, currentLine));
|
||||
} else if (value.startsWith("\"") && value.endsWith("\"")) {
|
||||
//Handle string
|
||||
|
||||
//Unescape the string
|
||||
String string = value.substring(1, value.length() - 1);
|
||||
|
||||
Matcher m = STRING_PATTERN.matcher(value);
|
||||
if (m.lookingAt()) {
|
||||
// Parse string
|
||||
String string = m.group(1);
|
||||
try {
|
||||
string = StringEscapeUtils.unescapeJava(string);
|
||||
} catch (IllegalArgumentException e) {
|
||||
@ -169,39 +164,39 @@ public class Assembler {
|
||||
"Invalid string operand \"" + string + "\": " + e.getMessage(),
|
||||
currentLine);
|
||||
}
|
||||
|
||||
out.write(string.getBytes(StandardCharsets.UTF_16BE));
|
||||
} else if (labels != null && labels.containsKey(value)) {
|
||||
//Handle label
|
||||
out.writeChar(labels.get(value));
|
||||
|
||||
} else {
|
||||
//Handle integer value
|
||||
continue;
|
||||
}
|
||||
int factor;
|
||||
if (m.usePattern(DUP_PATTERN).lookingAt()) {
|
||||
// Get DUP factor
|
||||
TokenParser parser = new TokenParser(m.group(1), currentLine, new HashMap<>());
|
||||
try {
|
||||
out.writeChar(Integer.decode(value));
|
||||
|
||||
} catch (NumberFormatException e) {
|
||||
//Handle assumed label
|
||||
if (labels == null) {
|
||||
|
||||
// Write placeholder word
|
||||
out.writeChar(0);
|
||||
|
||||
} else {
|
||||
|
||||
//Integer.decode failed, try binary
|
||||
if (value.startsWith("0b")) {
|
||||
try {
|
||||
out.writeChar(Integer.parseInt(value.substring(2), 2));
|
||||
} catch (NumberFormatException e2) {
|
||||
throw new InvalidOperandException("Invalid operand \"" + value + '"', currentLine);
|
||||
}
|
||||
} else {
|
||||
throw new InvalidOperandException("Invalid operand \"" + value + '"', currentLine);
|
||||
|
||||
}
|
||||
if (TokenParser.TokenType.Constant !=
|
||||
parser.getNextToken(true, TokenParser.ParseContext.Value)) {
|
||||
throw new InvalidOperandException("Invalid DUP factor " + m.group(1), currentLine);
|
||||
}
|
||||
} catch (AssemblyException ae) {
|
||||
throw new InvalidOperandException("Couldn't parse DUP factor " + m.group(1), currentLine);
|
||||
}
|
||||
factor = parser.lastInt;
|
||||
if (factor > MEM_SIZE) {
|
||||
throw new InvalidOperandException(
|
||||
"Factor '" + factor + "' exceeds total memory size", currentLine);
|
||||
}
|
||||
|
||||
value = m.group(2);
|
||||
}
|
||||
else {
|
||||
// Parse as single number
|
||||
factor = 1;
|
||||
}
|
||||
|
||||
Operand operand = new Operand(value, labels, registerSet, currentLine);
|
||||
char s = (char)operand.getData();
|
||||
for (int i = 0; i < factor; i++) {
|
||||
out.write(Util.getHigherByte(s));
|
||||
out.write(Util.getLowerByte(s));
|
||||
}
|
||||
}
|
||||
|
||||
@ -224,61 +219,10 @@ public class Assembler {
|
||||
* @param currentLine Current line number
|
||||
* @return Encoded instruction, null if the line is not a DW instruction
|
||||
*/
|
||||
private static byte[] parseDWInstruction(String line, int currentLine) throws AssemblyException {
|
||||
private byte[] parseDWInstruction(String line, int currentLine) throws AssemblyException {
|
||||
return parseDWInstruction(line, null, currentLine);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse the dup operator
|
||||
*
|
||||
* @param valueTokens Value tokens e.g. {"8", "DUP(12)"}
|
||||
* @param labels Map of labels
|
||||
* @return The encoded instruction
|
||||
*/
|
||||
private static byte[] parseDUPOperator16(String[] valueTokens, HashMap<String, Character> labels, int currentLine)
|
||||
throws InvalidOperandException {
|
||||
|
||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||
|
||||
try {
|
||||
|
||||
int factor = Integer.decode(valueTokens[0]);
|
||||
|
||||
if (factor > MEM_SIZE) {
|
||||
throw new InvalidOperandException("Factor '" + factor + "' exceeds total memory size", currentLine);
|
||||
}
|
||||
|
||||
String value = valueTokens[1].substring(4, valueTokens[1].lastIndexOf(')'));
|
||||
|
||||
//Handle label
|
||||
if (labels != null && labels.containsKey(value)) {
|
||||
//Label value is casted to byte
|
||||
for (int i = 0; i < factor; i++) {
|
||||
char s = labels.get(value);
|
||||
|
||||
out.write(Util.getHigherByte(s));
|
||||
out.write(Util.getLowerByte(s));
|
||||
}
|
||||
|
||||
} else {
|
||||
//Handle integer value
|
||||
char s = (char) (int) Integer.decode(value);
|
||||
|
||||
for (int i = 0; i < factor; i++) {
|
||||
out.write(Util.getHigherByte(s));
|
||||
out.write(Util.getLowerByte(s));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
} catch (NumberFormatException e) {
|
||||
throw new InvalidOperandException("Usage: <factor> DUP(<value>)", currentLine);
|
||||
}
|
||||
|
||||
return out.toByteArray();
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Check for and handle section declarations (.text and .data)
|
||||
*
|
||||
@ -289,13 +233,13 @@ public class Assembler {
|
||||
|
||||
String[] tokens = line.split("\\s+");
|
||||
|
||||
if (tokens[0].toUpperCase().equals(".TEXT")) {
|
||||
if (tokens[0].equalsIgnoreCase(".TEXT")) {
|
||||
|
||||
result.defineSection(Section.TEXT, currentLine, currentOffset);
|
||||
result.disassemblyLines.add(".text");
|
||||
throw new PseudoInstructionException(currentLine);
|
||||
|
||||
} else if (tokens[0].toUpperCase().equals(".DATA")) {
|
||||
} else if (tokens[0].equalsIgnoreCase(".DATA")) {
|
||||
|
||||
LogManager.LOGGER.fine("DEBUG: .data @" + currentLine);
|
||||
|
||||
@ -304,6 +248,9 @@ public class Assembler {
|
||||
}
|
||||
}
|
||||
|
||||
private static final Pattern EQU_PATTERN = Pattern.compile("([\\w]+)\\s+EQU\\s+(.+)",
|
||||
Pattern.CASE_INSENSITIVE);
|
||||
|
||||
/**
|
||||
* Check for and handle the EQU instruction
|
||||
*
|
||||
@ -311,30 +258,25 @@ public class Assembler {
|
||||
* @param labels Map of labels. Constants will be added as labels
|
||||
* @param currentLine Current line number
|
||||
*/
|
||||
private static void checkForEQUInstruction(String line, HashMap<String, Character> labels, int currentLine)
|
||||
private static void checkForEQUInstruction(String line, HashMap<String, Character> labels,
|
||||
int currentLine)
|
||||
throws AssemblyException {
|
||||
/* the EQU pseudo instruction is equivalent to the #define compiler directive in C/C++
|
||||
* usage: constant_name EQU <immediate_value>
|
||||
* A constant treated the same way as a label.
|
||||
*/
|
||||
line = line.trim();
|
||||
String[] tokens = line.split("\\s+");
|
||||
|
||||
Matcher matcher = EQU_PATTERN.matcher(line);
|
||||
|
||||
if (line.toUpperCase().matches(".*\\bEQU\\b.*")) {
|
||||
if (tokens[1].toUpperCase().equals("EQU") && tokens.length == 3) {
|
||||
try {
|
||||
//Save value as a label
|
||||
labels.put(tokens[0], (char) (int) Integer.decode(tokens[2]));
|
||||
} catch (NumberFormatException e) {
|
||||
throw new InvalidOperandException("Usage: constant_name EQU immediate_value", currentLine);
|
||||
}
|
||||
} else {
|
||||
throw new InvalidOperandException("Usage: constant_name EQU immediate_value", currentLine);
|
||||
}
|
||||
|
||||
if (matcher.lookingAt()) {
|
||||
//Save value as a label
|
||||
TokenParser parser = new TokenParser(matcher.group(2), currentLine, labels);
|
||||
char value = (char)parser.parseConstExpression();
|
||||
labels.put(matcher.group(1), value);
|
||||
throw new PseudoInstructionException(currentLine);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
@ -348,9 +290,6 @@ public class Assembler {
|
||||
* the errors, if any.
|
||||
*/
|
||||
public AssemblyResult parse(String text) {
|
||||
|
||||
int currentLine;
|
||||
|
||||
//Split in lines
|
||||
AssemblyResult result = new AssemblyResult(config);
|
||||
String[] lines = text.split("\n");
|
||||
@ -541,19 +480,15 @@ public class Assembler {
|
||||
|
||||
//Check for DW instruction
|
||||
try {
|
||||
if (assumeLabels) {
|
||||
byte[] bytes = parseDWInstruction(line, currentLine);
|
||||
if (bytes != null) {
|
||||
out.write(bytes);
|
||||
return out.toByteArray();
|
||||
}
|
||||
} else {
|
||||
byte[] bytes = parseDWInstruction(line, labels, currentLine);
|
||||
if (bytes != null) {
|
||||
out.write(bytes);
|
||||
return out.toByteArray();
|
||||
}
|
||||
byte[] bytes;
|
||||
if (assumeLabels) bytes = parseDWInstruction(line, currentLine);
|
||||
else bytes = parseDWInstruction(line, labels, currentLine);
|
||||
|
||||
if (bytes != null) {
|
||||
out.write(bytes);
|
||||
return out.toByteArray();
|
||||
}
|
||||
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
@ -1,5 +1,6 @@
|
||||
package net.simon987.mar.server.assembly;
|
||||
|
||||
import net.simon987.mar.server.assembly.exception.AssemblyException;
|
||||
import net.simon987.mar.server.assembly.exception.InvalidOperandException;
|
||||
|
||||
import java.util.HashMap;
|
||||
@ -25,8 +26,8 @@ public class Operand {
|
||||
private OperandType type;
|
||||
|
||||
/**
|
||||
* Value of the the operand, this is the part that will
|
||||
* written into the instruction.
|
||||
* Value of the operand, this is the part that will
|
||||
* be written into the instruction.
|
||||
*/
|
||||
private int value = 0;
|
||||
|
||||
@ -34,14 +35,14 @@ public class Operand {
|
||||
* Data of the operand. This will be appended after the instruction.
|
||||
* For example, "[AX+3]" value={index of AX] + {number of registers}, Data=3
|
||||
*/
|
||||
private int data = 0;
|
||||
private char data = 0;
|
||||
|
||||
public Operand(OperandType type, int value) {
|
||||
this.type = type;
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public Operand(OperandType type, int value, int data) {
|
||||
public Operand(OperandType type, int value, char data) {
|
||||
this(type, value);
|
||||
this.data = data;
|
||||
}
|
||||
@ -71,101 +72,56 @@ public class Operand {
|
||||
this.text = text.replace(",", "");
|
||||
this.text = this.text.trim();
|
||||
|
||||
if (parseReg(this.text, registerSet)) {
|
||||
return;
|
||||
}
|
||||
if (parseConstExpression(line, labels)) {
|
||||
type = OperandType.IMMEDIATE16;
|
||||
value = IMMEDIATE_VALUE;
|
||||
return;
|
||||
}
|
||||
if (this.text.startsWith("[") && this.text.endsWith("]")) {
|
||||
|
||||
if (!parseImmediate(this.text) && !parseReg(this.text, registerSet) && !parseLabel(this.text, labels)) {
|
||||
if (this.text.startsWith("[") && this.text.endsWith("]")) {
|
||||
|
||||
//Remove []s
|
||||
this.text = this.text.substring(1, this.text.length() - 1);
|
||||
|
||||
if (parseImmediate(this.text) || parseLabel(this.text, labels)) {
|
||||
//Operand refers to memory
|
||||
type = OperandType.MEMORY_IMM16;
|
||||
value = Operand.IMMEDIATE_VALUE_MEM;
|
||||
|
||||
} else if (!parseRegExpr(registerSet, labels)) {
|
||||
|
||||
if (labels == null) {
|
||||
type = OperandType.MEMORY_IMM16;
|
||||
data = 0;
|
||||
} else {
|
||||
throw new InvalidOperandException("Invalid operand " + this.text, line);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
} else {
|
||||
//Remove []s
|
||||
this.text = this.text.substring(1, this.text.length() - 1);
|
||||
if (parseConstExpression(line, labels)) {
|
||||
type = OperandType.MEMORY_IMM16;
|
||||
value = Operand.IMMEDIATE_VALUE_MEM;
|
||||
return;
|
||||
}
|
||||
if (!parseRegExpr(registerSet, labels, line)) {
|
||||
if (labels == null) {
|
||||
type = OperandType.IMMEDIATE16;
|
||||
type = OperandType.MEMORY_IMM16;
|
||||
data = 0;
|
||||
} else {
|
||||
throw new InvalidOperandException("Invalid operand " + this.text, line);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Attempt to parse an integer
|
||||
*
|
||||
* @param text Text to parse, can be a label or immediate value (hex or dec)
|
||||
* @return true if successful, false otherwise
|
||||
*/
|
||||
private boolean parseImmediate(String text) {
|
||||
|
||||
text = text.trim();
|
||||
|
||||
try {
|
||||
//Try IMM
|
||||
type = OperandType.IMMEDIATE16;
|
||||
data = Integer.decode(text);
|
||||
value = IMMEDIATE_VALUE;
|
||||
return true;
|
||||
} catch (NumberFormatException e) {
|
||||
|
||||
//Try Binary number (format 0bXXXX)
|
||||
if (text.startsWith("0b")) {
|
||||
try {
|
||||
data = Integer.parseInt(text.substring(2), 2);
|
||||
value = IMMEDIATE_VALUE;
|
||||
return true;
|
||||
} catch (NumberFormatException e2) {
|
||||
return false;
|
||||
}
|
||||
} else if (text.startsWith("0o")) {
|
||||
try {
|
||||
data = Integer.parseInt(text.substring(2), 8);
|
||||
value = IMMEDIATE_VALUE;
|
||||
return true;
|
||||
} catch (NumberFormatException e2) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempt to parse a user-defined label
|
||||
*
|
||||
* @param text Text to parse
|
||||
* @param labels Map of labels
|
||||
* @return true if parsing is successful, false otherwise
|
||||
*/
|
||||
private boolean parseLabel(String text, HashMap<String, Character> labels) {
|
||||
|
||||
text = text.trim();
|
||||
|
||||
if (labels == null) {
|
||||
return false;
|
||||
} else if (labels.containsKey(text)) {
|
||||
type = OperandType.IMMEDIATE16;
|
||||
data = labels.get(text);
|
||||
value = IMMEDIATE_VALUE;
|
||||
return true;
|
||||
} else {
|
||||
if (labels == null) {
|
||||
type = OperandType.IMMEDIATE16;
|
||||
data = 0;
|
||||
} else {
|
||||
throw new InvalidOperandException("Invalid operand " + this.text, line);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses a constant expression made of operators, literals, and labels as a single immediate.
|
||||
* Sets data to this value.
|
||||
* @param line The current line of compilation
|
||||
* @param labels The labels known to the compiler
|
||||
* @return true on success, false otherwise.
|
||||
*/
|
||||
private boolean parseConstExpression(int line, HashMap<String, Character> labels) {
|
||||
TokenParser parser = new TokenParser(text, line, labels);
|
||||
if (labels == null) return false;
|
||||
try {
|
||||
data = parser.parseConstExpression();
|
||||
return true;
|
||||
} catch (AssemblyException ex) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@ -196,7 +152,7 @@ public class Operand {
|
||||
*
|
||||
* @return true if successful
|
||||
*/
|
||||
private boolean parseRegExpr(RegisterSet registerSet, HashMap<String, Character> labels) {
|
||||
private boolean parseRegExpr(RegisterSet registerSet, HashMap<String, Character> labels, int line) {
|
||||
|
||||
String expr;
|
||||
|
||||
@ -216,7 +172,10 @@ public class Operand {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (expr.replaceAll("\\s+", "").isEmpty()) {
|
||||
//Remove white space
|
||||
expr = expr.replaceAll("\\s+", "");
|
||||
|
||||
if (expr.isEmpty()) {
|
||||
//No data specified
|
||||
type = OperandType.MEMORY_REG16;
|
||||
value += registerSet.size(); //refers to memory.
|
||||
@ -224,64 +183,22 @@ public class Operand {
|
||||
return true;
|
||||
}
|
||||
|
||||
//Remove white space
|
||||
expr = expr.replaceAll("\\s+", "");
|
||||
|
||||
try {
|
||||
type = OperandType.MEMORY_REG_DISP16;
|
||||
|
||||
if (labels != null) {
|
||||
|
||||
Character address = labels.get(expr.replaceAll("[^A-Za-z0-9_]", ""));
|
||||
if (address != null) {
|
||||
data = (expr.startsWith("-")) ? -address : address;
|
||||
value += registerSet.size() * 2;//refers to memory with disp
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
//label is invalid
|
||||
data = Integer.decode(expr);
|
||||
value += registerSet.size() * 2; //refers to memory with disp
|
||||
if (!expr.startsWith("-") && !expr.startsWith("+")) {
|
||||
return false;
|
||||
}
|
||||
type = OperandType.MEMORY_REG_DISP16;
|
||||
value += registerSet.size() * 2;
|
||||
if (labels == null) {
|
||||
data = 0;
|
||||
return true;
|
||||
} catch (NumberFormatException e) {
|
||||
|
||||
//Integer.decode failed, try binary
|
||||
if (expr.startsWith("+0b")) {
|
||||
try {
|
||||
data = Integer.parseInt(expr.substring(3), 2);
|
||||
value += registerSet.size() * 2; //refers to memory with disp
|
||||
return true;
|
||||
} catch (NumberFormatException e2) {
|
||||
return false;
|
||||
}
|
||||
} else if (expr.startsWith("-0b")) {
|
||||
try {
|
||||
data = -Integer.parseInt(expr.substring(3), 2);
|
||||
value += registerSet.size() * 2; //refers to memory with disp
|
||||
return true;
|
||||
} catch (NumberFormatException e2) {
|
||||
return false;
|
||||
}
|
||||
} else if (expr.startsWith("+0o")) {
|
||||
try {
|
||||
data = Integer.parseInt(expr.substring(3), 8);
|
||||
value += registerSet.size() * 2; //refers to memory with disp
|
||||
return true;
|
||||
} catch (NumberFormatException e2) {
|
||||
return false;
|
||||
}
|
||||
} else if (expr.startsWith("-0o")) {
|
||||
try {
|
||||
data = -Integer.parseInt(expr.substring(3), 8);
|
||||
value += registerSet.size() * 2; //refers to memory with disp
|
||||
return true;
|
||||
} catch (NumberFormatException e2) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
expr = "0" + expr;
|
||||
TokenParser parser = new TokenParser(expr, line, labels);
|
||||
try {
|
||||
data = parser.parseConstExpression();
|
||||
return true;
|
||||
} catch (AssemblyException ex) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@ -294,7 +211,7 @@ public class Operand {
|
||||
return value;
|
||||
}
|
||||
|
||||
public int getData() {
|
||||
public char getData() {
|
||||
return data;
|
||||
}
|
||||
|
||||
|
486
src/main/java/net/simon987/mar/server/assembly/TokenParser.java
Normal file
486
src/main/java/net/simon987/mar/server/assembly/TokenParser.java
Normal file
@ -0,0 +1,486 @@
|
||||
package net.simon987.mar.server.assembly;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Stack;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import net.simon987.mar.server.assembly.exception.AssemblyException;
|
||||
|
||||
public class TokenParser {
|
||||
|
||||
public enum TokenType {
|
||||
Constant, BinaryOperator, UnaryOperator, GroupOperator, Space, EOF
|
||||
}
|
||||
|
||||
public enum BinaryOperatorType {
|
||||
Add("+", 40) {
|
||||
@Override
|
||||
public int apply(int a, int b) {
|
||||
return (a + b) & 0xFFFF;
|
||||
}
|
||||
},Sub("-", 40) {
|
||||
@Override
|
||||
public int apply(int a, int b) {
|
||||
return (a - b) & 0xFFFF;
|
||||
}
|
||||
}, Mul("*", 30) {
|
||||
@Override
|
||||
public int apply(int a, int b) {
|
||||
return (a * b) & 0xFFFF;
|
||||
}
|
||||
}, Div("/", 30) {
|
||||
@Override
|
||||
public int apply(int a, int b) {
|
||||
return (a / b) & 0xFFFF;
|
||||
}
|
||||
}, Rem("%", 30) {
|
||||
@Override
|
||||
public int apply(int a, int b) {
|
||||
return (a / b) & 0xFFFF;
|
||||
}
|
||||
}, Shl("<<", 50) {
|
||||
@Override
|
||||
public int apply(int a, int b) {
|
||||
if (b >= 16) return 0;
|
||||
return (a << b) & 0xFFFF;
|
||||
}
|
||||
}, Shr(">>", 50) {
|
||||
@Override
|
||||
public int apply(int a, int b) {
|
||||
if (b >= 16) return 0;
|
||||
return (a >>> b) & 0xFFFF;
|
||||
}
|
||||
}, Rol("<", 50) {
|
||||
@Override
|
||||
public int apply(int a, int b) {
|
||||
b &= 0x11;
|
||||
return ((a >>> (16-b)) | (a << b)) & 0xFFFF;
|
||||
}
|
||||
}, Ror(">", 50) {
|
||||
@Override
|
||||
public int apply(int a, int b) {
|
||||
b &= 0x11;
|
||||
return ((a >>> b) | (a << (16-b))) & 0xFFFF;
|
||||
}
|
||||
}, Or("|", 80) {
|
||||
@Override
|
||||
public int apply(int a, int b) {
|
||||
return (a | b) & 0xFFFF;
|
||||
}
|
||||
}, And("&", 60) {
|
||||
@Override
|
||||
public int apply(int a, int b) {
|
||||
return (a & b) & 0xFFFF;
|
||||
}
|
||||
}, Xor("^", 70) {
|
||||
@Override
|
||||
public int apply(int a, int b) {
|
||||
return (a ^ b) & 0xFFFF;
|
||||
}
|
||||
};
|
||||
|
||||
final public static Map<String, BinaryOperatorType> stringMap = new HashMap<>();
|
||||
|
||||
static {
|
||||
for (BinaryOperatorType op : values()) {
|
||||
stringMap.put(op.symbol, op);
|
||||
}
|
||||
}
|
||||
|
||||
public final String symbol;
|
||||
/**
|
||||
* An operator with higher precedence is evaluated first
|
||||
*/
|
||||
public final int precedence;
|
||||
|
||||
BinaryOperatorType(final String symbol, final int precedence) {
|
||||
this.symbol = symbol;
|
||||
this.precedence = precedence;
|
||||
}
|
||||
|
||||
public abstract int apply(int a, int b);
|
||||
|
||||
}
|
||||
|
||||
public enum UnaryOperatorType {
|
||||
Neg("-") {
|
||||
@Override
|
||||
public int apply(int a) {
|
||||
return -a;
|
||||
}
|
||||
}, Not("~") {
|
||||
@Override
|
||||
public int apply(int a) {
|
||||
return ~a;
|
||||
}
|
||||
};
|
||||
|
||||
public static final Map<String, UnaryOperatorType> stringMap = new HashMap<>();
|
||||
|
||||
static {
|
||||
for (UnaryOperatorType op : values()) stringMap.put(op.symbol, op);
|
||||
}
|
||||
|
||||
public final String symbol;
|
||||
|
||||
UnaryOperatorType(final String symbol) {
|
||||
this.symbol = symbol;
|
||||
}
|
||||
public abstract int apply(int a);
|
||||
}
|
||||
|
||||
public enum GroupOperatorType {
|
||||
GroupStart("(", false, 0),
|
||||
GroupEnd(")", true, 0);
|
||||
|
||||
public static final Map<String, GroupOperatorType> stringMap = new HashMap<>();
|
||||
|
||||
static {
|
||||
for (GroupOperatorType op : values()) stringMap.put(op.symbol, op);
|
||||
}
|
||||
|
||||
public final String symbol;
|
||||
public final boolean end;
|
||||
public final int groupType;
|
||||
|
||||
GroupOperatorType(final String symbol, boolean end, int groupType) {
|
||||
this.symbol = symbol;
|
||||
this.end = end;
|
||||
this.groupType = groupType;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Interface allowing parse states to be manipulated, evaluated, and stacked.
|
||||
*/
|
||||
private static class ParseOperator {
|
||||
public int getPrecedence() {
|
||||
return 0;
|
||||
}
|
||||
public int apply(int other) {
|
||||
return other;
|
||||
}
|
||||
|
||||
public final int closeExpect;
|
||||
|
||||
public ParseOperator(int closeExpect) {
|
||||
this.closeExpect = closeExpect;
|
||||
}
|
||||
}
|
||||
|
||||
private static class ParseOperatorUnary extends ParseOperator {
|
||||
TokenParser.UnaryOperatorType op;
|
||||
@Override
|
||||
public int getPrecedence() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int apply(int other) {
|
||||
return op.apply(other);
|
||||
}
|
||||
|
||||
public ParseOperatorUnary(int closeExpect, TokenParser.UnaryOperatorType op) {
|
||||
super(closeExpect);
|
||||
this.op = op;
|
||||
}
|
||||
}
|
||||
|
||||
private static class ParseOperatorBinary extends ParseOperator {
|
||||
private final TokenParser.BinaryOperatorType op;
|
||||
private final int value;
|
||||
@Override
|
||||
public int getPrecedence() {
|
||||
return op.precedence;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int apply(int other) {
|
||||
return op.apply(value, other);
|
||||
}
|
||||
|
||||
public ParseOperatorBinary(int closeExpect, TokenParser.BinaryOperatorType op, int value) {
|
||||
super(closeExpect);
|
||||
this.op = op;
|
||||
this.value = value;
|
||||
}
|
||||
}
|
||||
|
||||
public enum ParseContext {
|
||||
TackOn, Value
|
||||
}
|
||||
|
||||
private static final Pattern BINARY_OPERATOR_PATTERN = Pattern.compile("(<<|>>|[\\-+*/<>&|^])");
|
||||
private static final Pattern UNARY_OPERATOR_PATTERN = Pattern.compile("([\\-~])");
|
||||
private static final Pattern GROUP_OPERATOR_PATTERN = Pattern.compile("([()])");
|
||||
private static final Pattern IDENTIFIER_PATTERN = Pattern.compile("([\\w]+)");
|
||||
private static final Pattern NUMBER_PATTERN_16 = Pattern.compile("(?:0[xX]|#)([\\da-fA-F]+)(?![\\w\\d])");
|
||||
private static final Pattern NUMBER_PATTERN_8 = Pattern.compile("0[oO]?([0-7]+)(?![\\w\\d])");
|
||||
private static final Pattern NUMBER_PATTERN_2 = Pattern.compile("0[bB]([01]+)(?![\\w\\d])");
|
||||
private static final Pattern NUMBER_PATTERN_10 = Pattern.compile("([1-9][\\d]*|0)(?![\\w\\d])");
|
||||
private static final Pattern NUMBER_PATTERN_START = Pattern.compile("([+]?)[\\d#]");
|
||||
private static final Pattern RESET_PATTERN = Pattern.compile("[^\\w\\d]");
|
||||
private static final Pattern SPACE_PATTERN = Pattern.compile("[^\\S\\n]+");
|
||||
|
||||
/**
|
||||
* @param sequence The characters to parse
|
||||
* @param start The index of the first character to be parsed
|
||||
* @param end The index of the character after the last character to be parsed
|
||||
*/
|
||||
|
||||
public TokenParser(CharSequence sequence, int line, Map<String, Character> labels, int start, int end) {
|
||||
matcher = SPACE_PATTERN.matcher(sequence);
|
||||
this.line = line;
|
||||
this.start = start;
|
||||
this.end = end;
|
||||
this.labels = labels;
|
||||
}
|
||||
|
||||
public TokenParser(CharSequence sequence, int line, Map<String, Character> labels) {
|
||||
this(sequence, line, labels,0, sequence.length());
|
||||
}
|
||||
|
||||
public int line, start, end;
|
||||
|
||||
private final Map<String, Character> labels;
|
||||
|
||||
private final Matcher matcher;
|
||||
|
||||
public int lastInt;
|
||||
|
||||
public BinaryOperatorType lastBinary;
|
||||
|
||||
public UnaryOperatorType lastUnary;
|
||||
|
||||
public GroupOperatorType lastGroup;
|
||||
|
||||
/**
|
||||
* Reads the next token.
|
||||
*
|
||||
* @param eatSpace whether the parser should ignore leading spaces
|
||||
* @param context the current class of tokens expected
|
||||
* @return The token that was found
|
||||
* @throws AssemblyException if an unrecognized token is found,
|
||||
* or if the found token is not supported in the current context.
|
||||
*/
|
||||
public TokenType getNextToken(boolean eatSpace, ParseContext context) throws AssemblyException {
|
||||
if (start >= end) {
|
||||
return TokenType.EOF;
|
||||
}
|
||||
matcher.region(start, end);
|
||||
|
||||
//Handle leading spaces
|
||||
if (matcher.usePattern(SPACE_PATTERN).lookingAt()) {
|
||||
start = matcher.end();
|
||||
if (!eatSpace) {
|
||||
return TokenType.Space;
|
||||
} else if (start >= end) {
|
||||
return TokenType.EOF;
|
||||
}
|
||||
matcher.region(start, end);
|
||||
}
|
||||
|
||||
//Group operators can occur in both contexts, i.e.:
|
||||
// ... + ( ...
|
||||
// ... y ) ...
|
||||
matcher.usePattern(GROUP_OPERATOR_PATTERN);
|
||||
if (matcher.lookingAt()) {
|
||||
start = matcher.end(1);
|
||||
String symbol = matcher.group(1);
|
||||
lastGroup = GroupOperatorType.stringMap.get(symbol);
|
||||
|
||||
//Should never happen unless the regex does not agree with GroupOperatorType.
|
||||
if (lastGroup == null) throw new AssemblyException("Group operator not supported", line);
|
||||
|
||||
return TokenType.GroupOperator;
|
||||
}
|
||||
if (context == ParseContext.TackOn) {
|
||||
if (matcher.usePattern(BINARY_OPERATOR_PATTERN).lookingAt()) {
|
||||
start = matcher.end();
|
||||
String symbol = matcher.group(1);
|
||||
lastBinary = BinaryOperatorType.stringMap.get(symbol);
|
||||
|
||||
//Should never happen unless the regex does not agree with BinaryOperatorType.
|
||||
if (lastBinary == null) throw new AssemblyException("Binary operator not supported", line);
|
||||
|
||||
return TokenType.BinaryOperator;
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (matcher.usePattern(NUMBER_PATTERN_START).lookingAt()) {
|
||||
start = matcher.end(1);
|
||||
matcher.region(start, end);
|
||||
try {
|
||||
//Attempt all supported radixes
|
||||
if (matcher.usePattern(NUMBER_PATTERN_10).lookingAt()) {
|
||||
lastInt = Integer.parseInt(matcher.group(1), 10);
|
||||
} else if (matcher.usePattern(NUMBER_PATTERN_16).lookingAt()) {
|
||||
lastInt = Integer.parseInt(matcher.group(1), 16);
|
||||
} else if (matcher.usePattern(NUMBER_PATTERN_2).lookingAt()) {
|
||||
lastInt = Integer.parseInt(matcher.group(1), 2);
|
||||
} else if (matcher.usePattern(NUMBER_PATTERN_8).lookingAt()) {
|
||||
lastInt = Integer.parseInt(matcher.group(1), 8);
|
||||
} else {
|
||||
if (matcher.usePattern(RESET_PATTERN).find()) {
|
||||
start = matcher.start();
|
||||
} else {
|
||||
start = end;
|
||||
}
|
||||
throw new AssemblyException("Invalid number found.", line);
|
||||
}
|
||||
} catch (NumberFormatException ex) {
|
||||
start = matcher.end(1);
|
||||
throw new AssemblyException("Number parsing failed", line);
|
||||
}
|
||||
start = matcher.end(1);
|
||||
lastInt &= 0xFFFF;
|
||||
return TokenType.Constant;
|
||||
}
|
||||
if (matcher.usePattern(IDENTIFIER_PATTERN).lookingAt()) {
|
||||
start = matcher.end(1);
|
||||
String identifier = matcher.group(1);
|
||||
Character val = labels.get(identifier);
|
||||
|
||||
if (val == null) {
|
||||
throw new AssemblyException("Unknown label found", line);
|
||||
}
|
||||
|
||||
lastInt = val;
|
||||
return TokenType.Constant;
|
||||
}
|
||||
matcher.usePattern(UNARY_OPERATOR_PATTERN);
|
||||
if (matcher.lookingAt()) {
|
||||
start = matcher.end(1);
|
||||
String symbol = matcher.group(1);
|
||||
lastUnary = UnaryOperatorType.stringMap.get(symbol);
|
||||
|
||||
// Should never happen unless the regex does not agree with UnaryOperatorType.
|
||||
if (lastUnary == null) {
|
||||
throw new AssemblyException("Unary operator not supported", line);
|
||||
}
|
||||
return TokenType.UnaryOperator;
|
||||
}
|
||||
}
|
||||
//For debug purposes, attempt skip to the next token in case of failure.
|
||||
//Not reliable enough for a debug tool.
|
||||
matcher.usePattern(RESET_PATTERN);
|
||||
if (matcher.lookingAt()) {
|
||||
start = matcher.end();
|
||||
} else if (matcher.find()) {
|
||||
start = matcher.end();
|
||||
} else {
|
||||
start = end;
|
||||
}
|
||||
throw new AssemblyException("Invalid token found", line);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses and evaluates a constant expression from the input.
|
||||
* @return the value of the constant expression
|
||||
* @throws AssemblyException when a constant expression cannot be parsed
|
||||
*/
|
||||
public char parseConstExpression()
|
||||
throws AssemblyException {
|
||||
Stack<ParseOperator> parseOps = new Stack<>();
|
||||
//A value of -1 indicates that no extra closing parenthesis are expected,
|
||||
//aside from those mandated by the stack.
|
||||
int closeExpect = -1;
|
||||
TokenParser.ParseContext context = TokenParser.ParseContext.Value;
|
||||
int lastValue = 0;
|
||||
while (true) {
|
||||
TokenParser.TokenType ty = getNextToken(true, context);
|
||||
if (context == TokenParser.ParseContext.Value) {
|
||||
//Parse value
|
||||
if (ty == TokenParser.TokenType.UnaryOperator) {
|
||||
//Add unary operator to the stack
|
||||
parseOps.push(new ParseOperatorUnary(closeExpect, lastUnary));
|
||||
closeExpect = -1;
|
||||
} else if (ty == TokenParser.TokenType.GroupOperator) {
|
||||
if (lastGroup.end) {
|
||||
throw new AssemblyException("Unexpected group close", line);
|
||||
}
|
||||
//No need to push a pointless operator.
|
||||
if (closeExpect != -1) {
|
||||
parseOps.push(new ParseOperator(closeExpect));
|
||||
}
|
||||
closeExpect = lastGroup.groupType;
|
||||
} else if (ty == TokenParser.TokenType.Constant) {
|
||||
lastValue = lastInt;
|
||||
//Look for binary operators next
|
||||
context = TokenParser.ParseContext.TackOn;
|
||||
} else {
|
||||
throw new AssemblyException("Value not found", line);
|
||||
}
|
||||
} else {
|
||||
//Parse modifier
|
||||
if (ty == TokenParser.TokenType.EOF || ty == TokenParser.TokenType.GroupOperator) {
|
||||
if (ty == TokenParser.TokenType.GroupOperator && !lastGroup.end) {
|
||||
throw new AssemblyException("Unexpected group open", line);
|
||||
}
|
||||
|
||||
if (closeExpect != -1) {
|
||||
if (ty == TokenParser.TokenType.EOF) {
|
||||
throw new AssemblyException("Unclosed group", line);
|
||||
} else if (closeExpect != lastGroup.groupType) {
|
||||
throw new AssemblyException("Unmatched group ends", line);
|
||||
} else {
|
||||
//New group operator closes the stored group operator
|
||||
closeExpect = -1;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
boolean completed = false;
|
||||
//Evaluation chain, repeats as long as there are operators that do not require closing operators.
|
||||
while (!parseOps.isEmpty()) {
|
||||
ParseOperator op = parseOps.pop();
|
||||
if (op.closeExpect != -1) {
|
||||
if (ty == TokenParser.TokenType.EOF) {
|
||||
throw new AssemblyException("Unclosed group", line);
|
||||
} else if (op.closeExpect != lastGroup.groupType) {
|
||||
throw new AssemblyException("Unmatched group ends", line);
|
||||
}
|
||||
//Found matching operator, stop evaluating.
|
||||
lastValue = op.apply(lastValue);
|
||||
completed = true;
|
||||
break;
|
||||
}
|
||||
lastValue = op.apply(lastValue);
|
||||
}
|
||||
if (!completed) {
|
||||
// There are no more operators
|
||||
if (ty == TokenParser.TokenType.EOF) {
|
||||
return (char)lastValue;
|
||||
} else if (lastGroup.groupType != -1) {
|
||||
throw new AssemblyException("Unexpected group close", line);
|
||||
}
|
||||
}
|
||||
} else if (ty == TokenParser.TokenType.BinaryOperator) {
|
||||
TokenParser.BinaryOperatorType bop = lastBinary;
|
||||
//Given the new operator's precedence,
|
||||
//it may now be possible to evaluate some previous stack items.
|
||||
while (closeExpect == -1 && !parseOps.empty()) {
|
||||
ParseOperator op = parseOps.peek();
|
||||
if (bop.precedence < op.getPrecedence()) {
|
||||
//The new operator is to be evaluated first.
|
||||
break;
|
||||
}
|
||||
lastValue = op.apply(lastValue);
|
||||
closeExpect = op.closeExpect;
|
||||
parseOps.pop();
|
||||
}
|
||||
parseOps.push(new ParseOperatorBinary(closeExpect, bop, lastValue));
|
||||
closeExpect = -1;
|
||||
context = TokenParser.ParseContext.Value;
|
||||
}
|
||||
else {
|
||||
throw new AssemblyException("Modifier or end not found", line);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -14,24 +14,16 @@ public class CodeRequestHandler implements MessageHandler {
|
||||
|
||||
LogManager.LOGGER.fine("(WS) Code request from " + user.getUser().getUsername());
|
||||
|
||||
if (user.getUser().isGuest()) {
|
||||
JSONObject response = new JSONObject();
|
||||
|
||||
JSONObject response = new JSONObject();
|
||||
response.put("t", "code");
|
||||
|
||||
response.put("t", "code");
|
||||
response.put("code", GameServer.INSTANCE.getConfig().getString("guest_user_code"));
|
||||
String code = user.getUser().isGuest() ?
|
||||
GameServer.INSTANCE.getConfig().getString("guest_user_code") :
|
||||
user.getUser().getUserCode();
|
||||
|
||||
user.getWebSocket().getRemote().sendString(response.toJSONString());
|
||||
|
||||
} else {
|
||||
|
||||
JSONObject response = new JSONObject();
|
||||
|
||||
response.put("t", "code");
|
||||
response.put("code", user.getUser().getUserCode());
|
||||
|
||||
user.getWebSocket().getRemote().sendString(response.toJSONString());
|
||||
}
|
||||
response.put("code", code);
|
||||
user.getWebSocket().getRemote().sendString(response.toJSONString());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -12,7 +12,7 @@ server_name=MAR dev
|
||||
# ALLOW | BLOCK
|
||||
guest_policy=ALLOW
|
||||
# DEBUG
|
||||
autologin=simon987
|
||||
#autologin=simon987
|
||||
|
||||
#Database
|
||||
mongo_dbname=mar_beta3
|
||||
|
@ -538,6 +538,8 @@ class GameClient {
|
||||
console.log("[MAR] Connected. Sent auth request");
|
||||
}
|
||||
|
||||
if (info.token == null) throw new Error("Auth token not found");
|
||||
|
||||
//Send auth request
|
||||
self.socket.send(info.token);
|
||||
|
||||
|
@ -17,7 +17,8 @@ enum TileType {
|
||||
VAULT_FLOOR,
|
||||
VAULT_WALL,
|
||||
FLUID,
|
||||
MAGNETIC
|
||||
MAGNETIC,
|
||||
VOID
|
||||
}
|
||||
|
||||
class Tile extends Phaser.Plugin.Isometric.IsoSprite {
|
||||
@ -70,7 +71,7 @@ class Tile extends Phaser.Plugin.Isometric.IsoSprite {
|
||||
return new VaultFloorTile(x, y);
|
||||
case TileType.VAULT_WALL:
|
||||
return new VaultWallTile(x, y);
|
||||
case -1:
|
||||
case TileType.VOID:
|
||||
return new VoidTile(x, y);
|
||||
case TileType.FLUID:
|
||||
return new FluidTile(x, y);
|
||||
|
20
src/main/typescript/phaser.d.ts
vendored
20
src/main/typescript/phaser.d.ts
vendored
@ -3697,19 +3697,19 @@ declare module Phaser {
|
||||
button: number;
|
||||
capture: boolean;
|
||||
callbackContext: any;
|
||||
event: MSPointerEvent;
|
||||
event: PointerEvent;
|
||||
game: Phaser.Game;
|
||||
input: Phaser.Input;
|
||||
|
||||
onPointerDown: (event: MSPointerEvent) => void;
|
||||
onPointerMove: (event: MSPointerEvent) => void;
|
||||
onPointerUp: (event: MSPointerEvent) => void;
|
||||
mouseDownCallback: (event: MSPointerEvent) => void;
|
||||
mouseMoveCallback: (event: MSPointerEvent) => void;
|
||||
mouseUpCallback: (event: MSPointerEvent) => void;
|
||||
pointerDownCallback: (event: MSPointerEvent) => void;
|
||||
pointerMoveCallback: (event: MSPointerEvent) => void;
|
||||
pointerUpCallback: (event: MSPointerEvent) => void;
|
||||
onPointerDown: (event: PointerEvent) => void;
|
||||
onPointerMove: (event: PointerEvent) => void;
|
||||
onPointerUp: (event: PointerEvent) => void;
|
||||
mouseDownCallback: (event: PointerEvent) => void;
|
||||
mouseMoveCallback: (event: PointerEvent) => void;
|
||||
mouseUpCallback: (event: PointerEvent) => void;
|
||||
pointerDownCallback: (event: PointerEvent) => void;
|
||||
pointerMoveCallback: (event: PointerEvent) => void;
|
||||
pointerUpCallback: (event: PointerEvent) => void;
|
||||
|
||||
start(): void;
|
||||
|
||||
|
@ -59,4 +59,14 @@ public class DwDirectiveTest {
|
||||
assertTrue(res.ar.exceptions.isEmpty());
|
||||
assertEquals('"' ,res.memValue(res.ar.origin));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void dupTest() {
|
||||
String code = "DW 10 DUP(4)";
|
||||
|
||||
TestExecutionResult res = TestHelper.executeCode(code);
|
||||
|
||||
assertTrue(res.ar.exceptions.isEmpty());
|
||||
assertEquals(4 ,res.memValue(res.ar.origin));
|
||||
}
|
||||
}
|
||||
|
@ -54,7 +54,7 @@ public class OperandTest {
|
||||
Operand imm2 = new Operand(" -12", labels, registerSet, 0);
|
||||
assertEquals(OperandType.IMMEDIATE16, imm2.getType());
|
||||
assertEquals(Operand.IMMEDIATE_VALUE, imm2.getValue());
|
||||
assertEquals(-12, imm2.getData());
|
||||
assertEquals((char)-12, imm2.getData());
|
||||
|
||||
Operand imm3 = new Operand(" 0xABCD", labels, registerSet, 0);
|
||||
assertEquals(OperandType.IMMEDIATE16, imm3.getType());
|
||||
@ -75,7 +75,7 @@ public class OperandTest {
|
||||
Operand mem2 = new Operand("[-12 ]", labels, registerSet, 0);
|
||||
assertEquals(OperandType.MEMORY_IMM16, mem2.getType());
|
||||
assertEquals(Operand.IMMEDIATE_VALUE_MEM, mem2.getValue());
|
||||
assertEquals(-12, mem2.getData());
|
||||
assertEquals((char)-12, mem2.getData());
|
||||
|
||||
Operand mem3 = new Operand(" [ 0xABCD]", labels, registerSet, 0);
|
||||
assertEquals(OperandType.MEMORY_IMM16, mem3.getType());
|
||||
@ -117,7 +117,7 @@ public class OperandTest {
|
||||
Operand mem10 = new Operand("[ B - label1 ]", labels, registerSet, 0);
|
||||
assertEquals(OperandType.MEMORY_REG_DISP16, mem10.getType());
|
||||
assertEquals(2 + 2 * registerSet.size(), mem10.getValue());
|
||||
assertEquals(-10, mem10.getData());
|
||||
assertEquals((char)-10, mem10.getData());
|
||||
|
||||
|
||||
} catch (InvalidOperandException e) {
|
||||
@ -165,6 +165,7 @@ public class OperandTest {
|
||||
fail();
|
||||
} catch (InvalidOperandException ignored) {
|
||||
}
|
||||
/* Tests disabled due to compile time constant folding.
|
||||
try {
|
||||
new Operand("[- 12]", labels, registerSet, 0);
|
||||
fail();
|
||||
@ -174,7 +175,7 @@ public class OperandTest {
|
||||
new Operand("[12+1]", labels, registerSet, 0);
|
||||
fail();
|
||||
} catch (InvalidOperandException ignored) {
|
||||
}
|
||||
} */
|
||||
try {
|
||||
new Operand("[+label1", labels, registerSet, 0);
|
||||
fail();
|
||||
@ -200,11 +201,13 @@ public class OperandTest {
|
||||
fail();
|
||||
} catch (InvalidOperandException ignored) {
|
||||
}
|
||||
/* This should most likely be allowed under constant folding.
|
||||
try {
|
||||
new Operand("[A + -1]", labels, registerSet, 0);
|
||||
fail();
|
||||
} catch (InvalidOperandException ignored) {
|
||||
}
|
||||
*/
|
||||
try {
|
||||
new Operand("[A + ]", labels, registerSet, 0);
|
||||
fail();
|
||||
|
158
src/test/java/net/simon987/mar/server/assembly/TokenTest.java
Normal file
158
src/test/java/net/simon987/mar/server/assembly/TokenTest.java
Normal file
@ -0,0 +1,158 @@
|
||||
package net.simon987.mar.server.assembly;
|
||||
|
||||
import net.simon987.mar.server.assembly.exception.AssemblyException;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
public class TokenTest {
|
||||
@Test
|
||||
public void constantTokenParsing() throws Exception {
|
||||
Map<String, Character> labels = new HashMap<>();
|
||||
labels.put("alpha", (char)29);
|
||||
labels.put("beta", (char)28);
|
||||
labels.put("gamma", (char)-5);
|
||||
labels.put("epsilon", (char)0);
|
||||
TokenParser parser = new TokenParser(
|
||||
" 27 0x27 #27 0o27 027 0b11011 alpha beta gamma delta epsilon 0 1a 0xG 0o8 0b2",
|
||||
0, labels);
|
||||
|
||||
assertEquals(TokenParser.TokenType.Space, parser.getNextToken(false, TokenParser.ParseContext.Value));
|
||||
assertEquals(TokenParser.TokenType.Constant, parser.getNextToken(false, TokenParser.ParseContext.Value));
|
||||
assertEquals((char)27, parser.lastInt);
|
||||
assertEquals(TokenParser.TokenType.Space, parser.getNextToken(false, TokenParser.ParseContext.TackOn));
|
||||
assertEquals(TokenParser.TokenType.Constant, parser.getNextToken(true, TokenParser.ParseContext.Value));
|
||||
assertEquals((char)39, parser.lastInt);
|
||||
assertEquals(TokenParser.TokenType.Constant, parser.getNextToken(true, TokenParser.ParseContext.Value));
|
||||
assertEquals((char)39, parser.lastInt);
|
||||
assertEquals(TokenParser.TokenType.Constant, parser.getNextToken(true, TokenParser.ParseContext.Value));
|
||||
assertEquals((char)23, parser.lastInt);
|
||||
assertEquals(TokenParser.TokenType.Constant, parser.getNextToken(true, TokenParser.ParseContext.Value));
|
||||
assertEquals((char)23, parser.lastInt);
|
||||
assertEquals(TokenParser.TokenType.Constant, parser.getNextToken(true, TokenParser.ParseContext.Value));
|
||||
assertEquals((char)27, parser.lastInt);
|
||||
|
||||
// Labels
|
||||
assertEquals(TokenParser.TokenType.Constant, parser.getNextToken(true, TokenParser.ParseContext.Value));
|
||||
assertEquals((char)29, parser.lastInt);
|
||||
assertEquals(TokenParser.TokenType.Constant, parser.getNextToken(true, TokenParser.ParseContext.Value));
|
||||
assertEquals((char)28, parser.lastInt);
|
||||
assertEquals(TokenParser.TokenType.Constant, parser.getNextToken(true, TokenParser.ParseContext.Value));
|
||||
assertEquals((char)-5, parser.lastInt);
|
||||
try {
|
||||
parser.getNextToken(true, TokenParser.ParseContext.Value);
|
||||
fail();
|
||||
} catch (AssemblyException expected) {
|
||||
}
|
||||
try {
|
||||
parser.getNextToken(true, TokenParser.ParseContext.TackOn);
|
||||
fail();
|
||||
} catch (AssemblyException expected) {
|
||||
}
|
||||
try {
|
||||
parser.getNextToken(true, TokenParser.ParseContext.TackOn);
|
||||
fail();
|
||||
} catch (AssemblyException expected) {
|
||||
}
|
||||
try {
|
||||
parser.getNextToken(true, TokenParser.ParseContext.Value);
|
||||
fail();
|
||||
} catch (AssemblyException expected) {
|
||||
}
|
||||
try {
|
||||
parser.getNextToken(true, TokenParser.ParseContext.Value);
|
||||
fail();
|
||||
} catch (AssemblyException expected) {
|
||||
}
|
||||
try {
|
||||
parser.getNextToken(true, TokenParser.ParseContext.Value);
|
||||
fail();
|
||||
} catch (AssemblyException expected) {
|
||||
}
|
||||
try {
|
||||
parser.getNextToken(true, TokenParser.ParseContext.Value);
|
||||
fail();
|
||||
} catch (AssemblyException expected) {
|
||||
}
|
||||
}
|
||||
@Test
|
||||
public void operatorTokenParsing() throws Exception {
|
||||
String ops = "+~-";
|
||||
TokenParser parser = new TokenParser(ops, 0, new HashMap<>());
|
||||
assertEquals(TokenParser.TokenType.BinaryOperator,
|
||||
parser.getNextToken(true, TokenParser.ParseContext.TackOn));
|
||||
assertEquals(TokenParser.BinaryOperatorType.Add, parser.lastBinary);
|
||||
try {
|
||||
parser.getNextToken(true, TokenParser.ParseContext.TackOn);
|
||||
fail();
|
||||
} catch (AssemblyException expected) {}
|
||||
assertEquals(TokenParser.TokenType.BinaryOperator,
|
||||
parser.getNextToken(true, TokenParser.ParseContext.TackOn));
|
||||
assertEquals(TokenParser.BinaryOperatorType.Sub, parser.lastBinary);
|
||||
|
||||
|
||||
parser = new TokenParser(ops, 0, new HashMap<>());
|
||||
try {
|
||||
parser.getNextToken(true, TokenParser.ParseContext.Value);
|
||||
fail();
|
||||
} catch (AssemblyException expected) {}
|
||||
assertEquals(TokenParser.TokenType.UnaryOperator,
|
||||
parser.getNextToken(true, TokenParser.ParseContext.Value));
|
||||
assertEquals(TokenParser.UnaryOperatorType.Not, parser.lastUnary);
|
||||
assertEquals(TokenParser.TokenType.UnaryOperator,
|
||||
parser.getNextToken(true, TokenParser.ParseContext.Value));
|
||||
assertEquals(TokenParser.UnaryOperatorType.Neg, parser.lastUnary);
|
||||
parser = new TokenParser("()", 0, new HashMap<>());
|
||||
assertEquals(TokenParser.TokenType.GroupOperator,
|
||||
parser.getNextToken(true, TokenParser.ParseContext.Value));
|
||||
assertEquals(TokenParser.GroupOperatorType.GroupStart, parser.lastGroup);
|
||||
assertEquals(TokenParser.TokenType.GroupOperator,
|
||||
parser.getNextToken(true, TokenParser.ParseContext.Value));
|
||||
assertEquals(TokenParser.GroupOperatorType.GroupEnd, parser.lastGroup);
|
||||
}
|
||||
|
||||
private void assertParse(int expect, String in) throws AssemblyException {
|
||||
assertEquals(expect, new TokenParser(in,0, new HashMap<>()).parseConstExpression());
|
||||
}
|
||||
|
||||
private void failParse(String in) {
|
||||
try {
|
||||
new TokenParser(in,0, new HashMap<>()).parseConstExpression();
|
||||
fail();
|
||||
} catch (AssemblyException ignore) {}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void parseTest() throws AssemblyException {
|
||||
assertParse(10, "10");
|
||||
assertParse(16, "0x10");
|
||||
assertParse(8, "0o10");
|
||||
assertParse(2, "0b10");
|
||||
assertParse(2, "1 + (1)");
|
||||
assertParse(98, "(10) * 9 + 8");
|
||||
assertParse(82, "10 + (9) * 8");
|
||||
assertParse(2, "(1 + 1)");
|
||||
assertParse(152, "((10 + 9)) * 8");
|
||||
assertParse(170, "(10 * ((9 + 8)))");
|
||||
assertParse((char)-170, "(-10 * ((9 + 8)))");
|
||||
assertParse((char)-170, "(10 * -((9 + 8)))");
|
||||
assertParse(170, "(-10 * -((9 + 8)))");
|
||||
assertParse(2, "(-3 + 5)");
|
||||
assertParse(3, "10 - 4 - 3");
|
||||
|
||||
failParse("1)");
|
||||
failParse("(1");
|
||||
failParse("(1 + 1");
|
||||
failParse("1 + 1)");
|
||||
failParse("((1 + 1)");
|
||||
failParse("(1 + 1))");
|
||||
failParse("+ 1");
|
||||
failParse("(+ 1)");
|
||||
failParse("(1 +)");
|
||||
failParse("1 + ()");
|
||||
failParse("() + 1");
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user