EQU and Mnemonic constant folding support.

This commit is contained in:
chromapid 2021-12-20 16:03:14 -07:00
parent 23ddff6eb2
commit 74ab5b6d88
10 changed files with 316 additions and 177 deletions

View File

@ -304,6 +304,10 @@ public class Assembler {
} }
} }
private static final Pattern EQU_PATTERN = Pattern.compile("([\\w]+)[^\\S\\n]+EQU[^\\S\\n]+(.+)",
Pattern.CASE_INSENSITIVE
);
/** /**
* Check for and handle the EQU instruction * Check for and handle the EQU instruction
* *
@ -311,30 +315,25 @@ public class Assembler {
* @param labels Map of labels. Constants will be added as labels * @param labels Map of labels. Constants will be added as labels
* @param currentLine Current line number * @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, RegisterSet registers)
throws AssemblyException { throws AssemblyException {
/* the EQU pseudo instruction is equivalent to the #define compiler directive in C/C++ /* the EQU pseudo instruction is equivalent to the #define compiler directive in C/C++
* usage: constant_name EQU <immediate_value> * usage: constant_name EQU <immediate_value>
* A constant treated the same way as a label. * A constant treated the same way as a label.
*/ */
line = line.trim(); line = line.trim();
String[] tokens = line.split("\\s+");
Matcher matcher = EQU_PATTERN.matcher(line);
if (line.toUpperCase().matches(".*\\bEQU\\b.*")) { if (matcher.lookingAt()) {
if (tokens[1].toUpperCase().equals("EQU") && tokens.length == 3) { //Save value as a label
try { TokenParser parser = new TokenParser(matcher.group(2), currentLine, labels);
//Save value as a label char value = (char)parser.parseConstExpression();
labels.put(tokens[0], (char) (int) Integer.decode(tokens[2])); labels.put(matcher.group(1), value);
} catch (NumberFormatException e) {
throw new InvalidOperandException("Usage: constant_name EQU immediate_value", currentLine);
}
} else {
throw new InvalidOperandException("Usage: constant_name EQU immediate_value", currentLine);
}
throw new PseudoInstructionException(currentLine); throw new PseudoInstructionException(currentLine);
} }
} }
/** /**
@ -452,7 +451,7 @@ public class Assembler {
//Check for pseudo instructions //Check for pseudo instructions
checkForSectionDeclaration(line, result, currentLine, currentOffset); checkForSectionDeclaration(line, result, currentLine, currentOffset);
checkForEQUInstruction(line, result.labels, currentLine); checkForEQUInstruction(line, result.labels, currentLine, registerSet);
checkForORGInstruction(line, result, currentLine); checkForORGInstruction(line, result, currentLine);
for (String label : result.labels.keySet()) { for (String label : result.labels.keySet()) {

View File

@ -27,8 +27,8 @@ public class Operand {
private OperandType type; private OperandType type;
/** /**
* Value of the the operand, this is the part that will * Value of the operand, this is the part that will
* written into the instruction. * be written into the instruction.
*/ */
private int value = 0; private int value = 0;
@ -73,40 +73,59 @@ public class Operand {
this.text = text.replace(",", ""); this.text = text.replace(",", "");
this.text = this.text.trim(); 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)) { //Remove []s
if (this.text.startsWith("[") && this.text.endsWith("]")) { this.text = this.text.substring(1, this.text.length() - 1);
if (parseConstExpression(line, labels)) {
//Remove []s type = OperandType.MEMORY_IMM16;
this.text = this.text.substring(1, this.text.length() - 1); value = Operand.IMMEDIATE_VALUE_MEM;
return;
if (parseImmediate(this.text) || parseLabel(this.text, labels)) { }
//Operand refers to memory if (!parseRegExpr(registerSet, labels)) {
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 {
if (labels == null) { if (labels == null) {
type = OperandType.IMMEDIATE16; type = OperandType.MEMORY_IMM16;
data = 0; data = 0;
} else { } else {
throw new InvalidOperandException("Invalid operand " + this.text, line); throw new InvalidOperandException("Invalid operand " + this.text, line);
} }
}
} 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;
}
}
/** /**
* Attempt to parse an integer * Attempt to parse an integer

View File

@ -2,6 +2,7 @@ package net.simon987.mar.server.assembly;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
import java.util.Stack;
import java.util.regex.Matcher; import java.util.regex.Matcher;
import java.util.regex.Pattern; import java.util.regex.Pattern;
@ -147,6 +148,64 @@ public class TokenParser {
} }
} }
/**
* 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 { public enum ParseContext {
TackOn, Value TackOn, Value
} }
@ -155,11 +214,11 @@ public class TokenParser {
private static final Pattern UNARY_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 GROUP_OPERATOR_PATTERN = Pattern.compile("([()])");
private static final Pattern IDENTIFIER_PATTERN = Pattern.compile("([\\w]+)"); 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_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_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_2 = Pattern.compile("0[bB]([01]+)(?![\\w\\d])");
private static final Pattern NUMBER_PATTERN_10 = Pattern.compile("(\\d+)(?![\\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 NUMBER_PATTERN_START = Pattern.compile("([+]?)[\\d#]");
private static final Pattern RESET_PATTERN = Pattern.compile("[^\\w\\d]"); private static final Pattern RESET_PATTERN = Pattern.compile("[^\\w\\d]");
private static final Pattern SPACE_PATTERN = Pattern.compile("[^\\S\\n]+"); private static final Pattern SPACE_PATTERN = Pattern.compile("[^\\S\\n]+");
@ -181,11 +240,11 @@ public class TokenParser {
this(sequence, line, labels,0, sequence.length()); this(sequence, line, labels,0, sequence.length());
} }
private int line, start, end; public int line, start, end;
private Map<String, Character> labels; private final Map<String, Character> labels;
private Matcher matcher; private final Matcher matcher;
public int lastInt; public int lastInt;
@ -238,19 +297,23 @@ public class TokenParser {
} }
else { else {
if (matcher.usePattern(NUMBER_PATTERN_START).lookingAt()) { if (matcher.usePattern(NUMBER_PATTERN_START).lookingAt()) {
start = matcher.end(1);
matcher.region(start, end);
try { try {
if (matcher.usePattern(NUMBER_PATTERN_10).lookingAt()) if (matcher.usePattern(NUMBER_PATTERN_10).lookingAt()) {
lastInt = Integer.parseInt(matcher.group(1), 10); lastInt = Integer.parseInt(matcher.group(1), 10);
else if (matcher.usePattern(NUMBER_PATTERN_16).lookingAt()) } else if (matcher.usePattern(NUMBER_PATTERN_16).lookingAt()) {
lastInt = Integer.parseInt(matcher.group(1), 16); lastInt = Integer.parseInt(matcher.group(1), 16);
else if (matcher.usePattern(NUMBER_PATTERN_2).lookingAt()) } else if (matcher.usePattern(NUMBER_PATTERN_2).lookingAt()) {
lastInt = Integer.parseInt(matcher.group(1), 2); lastInt = Integer.parseInt(matcher.group(1), 2);
else if (matcher.usePattern(NUMBER_PATTERN_8).lookingAt()) } else if (matcher.usePattern(NUMBER_PATTERN_8).lookingAt()) {
lastInt = Integer.parseInt(matcher.group(1), 8); lastInt = Integer.parseInt(matcher.group(1), 8);
else { } else {
if (matcher.usePattern(RESET_PATTERN).find()) start = matcher.start(); if (matcher.usePattern(RESET_PATTERN).find()) {
else start = end; start = matcher.start();
} else {
start = end;
}
throw new AssemblyException("Invalid number found.", line); throw new AssemblyException("Invalid number found.", line);
} }
} catch (NumberFormatException ex) { } catch (NumberFormatException ex) {
@ -266,7 +329,9 @@ public class TokenParser {
String identifier = matcher.group(1); String identifier = matcher.group(1);
Character val = labels.get(identifier); Character val = labels.get(identifier);
if (val == null) throw new AssemblyException("Unknown label found", line); if (val == null) {
throw new AssemblyException("Unknown label found", line);
}
lastInt = val; lastInt = val;
return TokenType.Constant; return TokenType.Constant;
@ -278,14 +343,110 @@ public class TokenParser {
lastUnary = UnaryOperatorType.stringMap.get(symbol); lastUnary = UnaryOperatorType.stringMap.get(symbol);
// Should never happen unless the regex does not agree with UnaryOperatorType. // Should never happen unless the regex does not agree with UnaryOperatorType.
if (lastUnary == null) throw new AssemblyException("Unary operator not supported", line); if (lastUnary == null) {
throw new AssemblyException("Unary operator not supported", line);
}
return TokenType.UnaryOperator; return TokenType.UnaryOperator;
} }
} }
if (matcher.usePattern(RESET_PATTERN).find()) start = matcher.end(); matcher.usePattern(RESET_PATTERN);
else start = end; if (matcher.lookingAt()) {
start = matcher.end();
} else if (matcher.find()) {
start = matcher.end();
} else {
start = end;
}
throw new AssemblyException("Invalid token found", line); throw new AssemblyException("Invalid token found", line);
} }
public int parseConstExpression()
throws AssemblyException {
Stack<ParseOperator> parseOps = new Stack<>();
int closeExpect = -1; // No closing parenthesis expected
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) {
parseOps.push(new ParseOperatorUnary(closeExpect, lastUnary));
closeExpect = -1;
} else if (ty == TokenParser.TokenType.GroupOperator) {
if (lastGroup.end) {
throw new AssemblyException("Unexpected group close", line);
}
if (closeExpect != -1) {
parseOps.push(new ParseOperator(closeExpect));
}
closeExpect = lastGroup.groupType;
} else if (ty == TokenParser.TokenType.Constant) {
lastValue = lastInt;
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 {
closeExpect = -1;
continue;
}
}
boolean completed = false;
//Evaluation chain
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);
}
lastValue = op.apply(lastValue);
completed = true;
break;
}
lastValue = op.apply(lastValue);
}
if (!completed) {
if (ty == TokenParser.TokenType.EOF) {
return lastValue;
} else if (lastGroup.groupType != -1) {
throw new AssemblyException("Unexpected group close", line);
}
}
} else if (ty == TokenParser.TokenType.BinaryOperator) {
TokenParser.BinaryOperatorType bop = lastBinary;
while (closeExpect == -1 && !parseOps.empty()) {
ParseOperator op = parseOps.peek();
if (bop.precedence <= op.getPrecedence()) {
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);
}
}
}
} }

View File

@ -14,24 +14,16 @@ public class CodeRequestHandler implements MessageHandler {
LogManager.LOGGER.fine("(WS) Code request from " + user.getUser().getUsername()); 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"); String code = user.getUser().isGuest() ?
response.put("code", GameServer.INSTANCE.getConfig().getString("guest_user_code")); GameServer.INSTANCE.getConfig().getString("guest_user_code") :
user.getUser().getUserCode();
user.getWebSocket().getRemote().sendString(response.toJSONString()); response.put("code", code);
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());
}
} }
} }
} }

View File

@ -12,7 +12,7 @@ server_name=MAR dev
# ALLOW | BLOCK # ALLOW | BLOCK
guest_policy=ALLOW guest_policy=ALLOW
# DEBUG # DEBUG
autologin=simon987 #autologin=simon987
#Database #Database
mongo_dbname=mar_beta3 mongo_dbname=mar_beta3

View File

@ -538,6 +538,8 @@ class GameClient {
console.log("[MAR] Connected. Sent auth request"); console.log("[MAR] Connected. Sent auth request");
} }
if (info.token == null) throw new Error("Auth token not found");
//Send auth request //Send auth request
self.socket.send(info.token); self.socket.send(info.token);

View File

@ -17,7 +17,8 @@ enum TileType {
VAULT_FLOOR, VAULT_FLOOR,
VAULT_WALL, VAULT_WALL,
FLUID, FLUID,
MAGNETIC MAGNETIC,
VOID
} }
class Tile extends Phaser.Plugin.Isometric.IsoSprite { class Tile extends Phaser.Plugin.Isometric.IsoSprite {
@ -70,7 +71,7 @@ class Tile extends Phaser.Plugin.Isometric.IsoSprite {
return new VaultFloorTile(x, y); return new VaultFloorTile(x, y);
case TileType.VAULT_WALL: case TileType.VAULT_WALL:
return new VaultWallTile(x, y); return new VaultWallTile(x, y);
case -1: case TileType.VOID:
return new VoidTile(x, y); return new VoidTile(x, y);
case TileType.FLUID: case TileType.FLUID:
return new FluidTile(x, y); return new FluidTile(x, y);

View File

@ -3697,19 +3697,19 @@ declare module Phaser {
button: number; button: number;
capture: boolean; capture: boolean;
callbackContext: any; callbackContext: any;
event: MSPointerEvent; event: PointerEvent;
game: Phaser.Game; game: Phaser.Game;
input: Phaser.Input; input: Phaser.Input;
onPointerDown: (event: MSPointerEvent) => void; onPointerDown: (event: PointerEvent) => void;
onPointerMove: (event: MSPointerEvent) => void; onPointerMove: (event: PointerEvent) => void;
onPointerUp: (event: MSPointerEvent) => void; onPointerUp: (event: PointerEvent) => void;
mouseDownCallback: (event: MSPointerEvent) => void; mouseDownCallback: (event: PointerEvent) => void;
mouseMoveCallback: (event: MSPointerEvent) => void; mouseMoveCallback: (event: PointerEvent) => void;
mouseUpCallback: (event: MSPointerEvent) => void; mouseUpCallback: (event: PointerEvent) => void;
pointerDownCallback: (event: MSPointerEvent) => void; pointerDownCallback: (event: PointerEvent) => void;
pointerMoveCallback: (event: MSPointerEvent) => void; pointerMoveCallback: (event: PointerEvent) => void;
pointerUpCallback: (event: MSPointerEvent) => void; pointerUpCallback: (event: PointerEvent) => void;
start(): void; start(): void;

View File

@ -165,6 +165,7 @@ public class OperandTest {
fail(); fail();
} catch (InvalidOperandException ignored) { } catch (InvalidOperandException ignored) {
} }
/* Tests disabled due to compile time constant folding.
try { try {
new Operand("[- 12]", labels, registerSet, 0); new Operand("[- 12]", labels, registerSet, 0);
fail(); fail();
@ -174,7 +175,7 @@ public class OperandTest {
new Operand("[12+1]", labels, registerSet, 0); new Operand("[12+1]", labels, registerSet, 0);
fail(); fail();
} catch (InvalidOperandException ignored) { } catch (InvalidOperandException ignored) {
} } */
try { try {
new Operand("[+label1", labels, registerSet, 0); new Operand("[+label1", labels, registerSet, 0);
fail(); fail();

View File

@ -17,7 +17,7 @@ public class TokenTest {
labels.put("gamma", (char)-5); labels.put("gamma", (char)-5);
labels.put("epsilon", (char)0); labels.put("epsilon", (char)0);
TokenParser parser = new TokenParser( TokenParser parser = new TokenParser(
" 27 0x27 0o27 0b11011 alpha beta gamma delta epsilon 0 1a 0xG 0o8 0b2", " 27 0x27 #27 0o27 027 0b11011 alpha beta gamma delta epsilon 0 1a 0xG 0o8 0b2",
0, labels); 0, labels);
assertEquals(TokenParser.TokenType.Space, parser.getNextToken(false, TokenParser.ParseContext.Value)); assertEquals(TokenParser.TokenType.Space, parser.getNextToken(false, TokenParser.ParseContext.Value));
@ -27,6 +27,10 @@ public class TokenTest {
assertEquals(TokenParser.TokenType.Constant, parser.getNextToken(true, TokenParser.ParseContext.Value)); assertEquals(TokenParser.TokenType.Constant, parser.getNextToken(true, TokenParser.ParseContext.Value));
assertEquals((char)39, parser.lastInt); assertEquals((char)39, parser.lastInt);
assertEquals(TokenParser.TokenType.Constant, parser.getNextToken(true, TokenParser.ParseContext.Value)); 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((char)23, parser.lastInt);
assertEquals(TokenParser.TokenType.Constant, parser.getNextToken(true, TokenParser.ParseContext.Value)); assertEquals(TokenParser.TokenType.Constant, parser.getNextToken(true, TokenParser.ParseContext.Value));
assertEquals((char)27, parser.lastInt); assertEquals((char)27, parser.lastInt);
@ -41,31 +45,38 @@ public class TokenTest {
try { try {
parser.getNextToken(true, TokenParser.ParseContext.Value); parser.getNextToken(true, TokenParser.ParseContext.Value);
fail(); fail();
} catch (AssemblyException ignore) {} } catch (AssemblyException expected) {
}
try { try {
parser.getNextToken(true, TokenParser.ParseContext.TackOn); parser.getNextToken(true, TokenParser.ParseContext.TackOn);
fail(); fail();
} catch (AssemblyException ignore) {} } catch (AssemblyException expected) {
}
try { try {
parser.getNextToken(true, TokenParser.ParseContext.TackOn); parser.getNextToken(true, TokenParser.ParseContext.TackOn);
fail(); fail();
} catch (AssemblyException ignore) {} } catch (AssemblyException expected) {
}
try { try {
parser.getNextToken(true, TokenParser.ParseContext.Value); parser.getNextToken(true, TokenParser.ParseContext.Value);
fail(); fail();
} catch (AssemblyException ignore) {} } catch (AssemblyException expected) {
}
try { try {
parser.getNextToken(true, TokenParser.ParseContext.Value); parser.getNextToken(true, TokenParser.ParseContext.Value);
fail(); fail();
} catch (AssemblyException ignore) {} } catch (AssemblyException expected) {
}
try { try {
parser.getNextToken(true, TokenParser.ParseContext.Value); parser.getNextToken(true, TokenParser.ParseContext.Value);
fail(); fail();
} catch (AssemblyException ignore) {} } catch (AssemblyException expected) {
}
try { try {
parser.getNextToken(true, TokenParser.ParseContext.Value); parser.getNextToken(true, TokenParser.ParseContext.Value);
fail(); fail();
} catch (AssemblyException ignore) {} } catch (AssemblyException expected) {
}
} }
@Test @Test
public void operatorTokenParsing() throws Exception { public void operatorTokenParsing() throws Exception {
@ -77,7 +88,7 @@ public class TokenTest {
try { try {
parser.getNextToken(true, TokenParser.ParseContext.TackOn); parser.getNextToken(true, TokenParser.ParseContext.TackOn);
fail(); fail();
} catch (AssemblyException ignore) {} } catch (AssemblyException expected) {}
assertEquals(TokenParser.TokenType.BinaryOperator, assertEquals(TokenParser.TokenType.BinaryOperator,
parser.getNextToken(true, TokenParser.ParseContext.TackOn)); parser.getNextToken(true, TokenParser.ParseContext.TackOn));
assertEquals(TokenParser.BinaryOperatorType.Sub, parser.lastBinary); assertEquals(TokenParser.BinaryOperatorType.Sub, parser.lastBinary);
@ -87,8 +98,7 @@ public class TokenTest {
try { try {
parser.getNextToken(true, TokenParser.ParseContext.Value); parser.getNextToken(true, TokenParser.ParseContext.Value);
fail(); fail();
} catch (AssemblyException ignore) {} } catch (AssemblyException expected) {}
assertEquals(TokenParser.TokenType.UnaryOperator, assertEquals(TokenParser.TokenType.UnaryOperator,
parser.getNextToken(true, TokenParser.ParseContext.Value)); parser.getNextToken(true, TokenParser.ParseContext.Value));
assertEquals(TokenParser.UnaryOperatorType.Not, parser.lastUnary); assertEquals(TokenParser.UnaryOperatorType.Not, parser.lastUnary);
@ -104,85 +114,39 @@ public class TokenTest {
assertEquals(TokenParser.GroupOperatorType.GroupEnd, parser.lastGroup); assertEquals(TokenParser.GroupOperatorType.GroupEnd, parser.lastGroup);
} }
@Test private void assertParse(int expect, String in) throws AssemblyException {
public void parseTest() throws AssemblyException { assertEquals(expect, new TokenParser(in,0, new HashMap<>()).parseConstExpression());
assertEquals(1, Operand.parseConstExpression( }
"1",
0, new HashMap<>()) private void failParse(String in) {
);
assertEquals(2, Operand.parseConstExpression(
"1 + 1",
0, new HashMap<>())
);
assertEquals(82, Operand.parseConstExpression(
"10 + 9 * 8",
0, new HashMap<>())
);
assertEquals(98, Operand.parseConstExpression(
"10 * 9 + 8",
0, new HashMap<>())
);
assertEquals(2, Operand.parseConstExpression(
"(1 + 1)",
0, new HashMap<>())
);
assertEquals(152, Operand.parseConstExpression(
"((10 + 9)) * 8",
0, new HashMap<>())
);
assertEquals(170, Operand.parseConstExpression(
"(10 * ((9 + 8)))",
0, new HashMap<>())
);
assertEquals((char)-170, Operand.parseConstExpression(
"(-10 * ((9 + 8)))",
0, new HashMap<>())
);
assertEquals(2, Operand.parseConstExpression(
"(-3 + 5)",
0, new HashMap<>())
);
try { try {
Operand.parseConstExpression( new TokenParser(in,0, new HashMap<>()).parseConstExpression();
"(1",
0, new HashMap<>()
);
fail();
} catch (AssemblyException ignore) {}
try {
Operand.parseConstExpression(
"1)",
0, new HashMap<>()
);
fail();
} catch (AssemblyException ignore) {}
try {
Operand.parseConstExpression(
"(1+1",
0, new HashMap<>()
);
fail();
} catch (AssemblyException ignore) {}
try {
Operand.parseConstExpression(
"1+1)",
0, new HashMap<>()
);
fail();
} catch (AssemblyException ignore) {}
try {
Operand.parseConstExpression(
"((1+1)",
0, new HashMap<>()
);
fail();
} catch (AssemblyException ignore) {}
try {
Operand.parseConstExpression(
"(1+1))",
0, new HashMap<>()
);
fail(); fail();
} catch (AssemblyException ignore) {} } 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)");
failParse("1)");
failParse("(1");
failParse("(1 + 1");
failParse("1 + 1)");
failParse("((1 + 1)");
failParse("(1 + 1))");
}
} }