Comments, more testing, and formatting.

This commit is contained in:
chromapid 2021-12-21 00:55:25 -07:00
parent 434abb0fb1
commit 874b7ae802
2 changed files with 51 additions and 13 deletions

View File

@ -84,10 +84,15 @@ public class TokenParser {
final public static Map<String, BinaryOperatorType> stringMap = new HashMap<>(); final public static Map<String, BinaryOperatorType> stringMap = new HashMap<>();
static { static {
for (BinaryOperatorType op : values()) stringMap.put(op.symbol, op); for (BinaryOperatorType op : values()) {
stringMap.put(op.symbol, op);
}
} }
public final String symbol; public final String symbol;
/**
* An operator with higher precedence is evaluated first
*/
public final int precedence; public final int precedence;
BinaryOperatorType(final String symbol, final int precedence) { BinaryOperatorType(final String symbol, final int precedence) {
@ -261,21 +266,32 @@ public class TokenParser {
* or if the found token is not supported in the current context. * or if the found token is not supported in the current context.
*/ */
public TokenType getNextToken(boolean eatSpace, ParseContext context) throws AssemblyException { public TokenType getNextToken(boolean eatSpace, ParseContext context) throws AssemblyException {
if (start >= end) return TokenType.EOF; if (start >= end) {
return TokenType.EOF;
}
matcher.region(start, end); matcher.region(start, end);
//Handle leading spaces
if (matcher.usePattern(SPACE_PATTERN).lookingAt()) { if (matcher.usePattern(SPACE_PATTERN).lookingAt()) {
start = matcher.end(); start = matcher.end();
if (!eatSpace) return TokenType.Space; if (!eatSpace) {
if (start >= end) return TokenType.EOF; return TokenType.Space;
} else if (start >= end) {
return TokenType.EOF;
}
matcher.region(start, end); matcher.region(start, end);
} }
//Group operators can occur in both contexts, i.e.:
// ... + ( ...
// ... y ) ...
matcher.usePattern(GROUP_OPERATOR_PATTERN); matcher.usePattern(GROUP_OPERATOR_PATTERN);
if (matcher.lookingAt()) { if (matcher.lookingAt()) {
start = matcher.end(1); start = matcher.end(1);
String symbol = matcher.group(1); String symbol = matcher.group(1);
lastGroup = GroupOperatorType.stringMap.get(symbol); lastGroup = GroupOperatorType.stringMap.get(symbol);
// Should never happen unless the regex does not agree with GroupOperatorType. //Should never happen unless the regex does not agree with GroupOperatorType.
if (lastGroup == null) throw new AssemblyException("Group operator not supported", line); if (lastGroup == null) throw new AssemblyException("Group operator not supported", line);
return TokenType.GroupOperator; return TokenType.GroupOperator;
@ -286,7 +302,7 @@ public class TokenParser {
String symbol = matcher.group(1); String symbol = matcher.group(1);
lastBinary = BinaryOperatorType.stringMap.get(symbol); lastBinary = BinaryOperatorType.stringMap.get(symbol);
// Should never happen unless the regex does not agree with BinaryOperatorType. //Should never happen unless the regex does not agree with BinaryOperatorType.
if (lastBinary == null) throw new AssemblyException("Binary operator not supported", line); if (lastBinary == null) throw new AssemblyException("Binary operator not supported", line);
return TokenType.BinaryOperator; return TokenType.BinaryOperator;
@ -297,6 +313,7 @@ public class TokenParser {
start = matcher.end(1); start = matcher.end(1);
matcher.region(start, end); matcher.region(start, end);
try { try {
//Attempt all supported radixes
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()) {
@ -346,6 +363,8 @@ public class TokenParser {
return TokenType.UnaryOperator; 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); matcher.usePattern(RESET_PATTERN);
if (matcher.lookingAt()) { if (matcher.lookingAt()) {
start = matcher.end(); start = matcher.end();
@ -365,50 +384,56 @@ public class TokenParser {
public char parseConstExpression() public char parseConstExpression()
throws AssemblyException { throws AssemblyException {
Stack<ParseOperator> parseOps = new Stack<>(); Stack<ParseOperator> parseOps = new Stack<>();
int closeExpect = -1; // No closing parenthesis expected //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; TokenParser.ParseContext context = TokenParser.ParseContext.Value;
int lastValue = 0; int lastValue = 0;
while (true) { while (true) {
TokenParser.TokenType ty = getNextToken(true, context); TokenParser.TokenType ty = getNextToken(true, context);
if (context == TokenParser.ParseContext.Value) { if (context == TokenParser.ParseContext.Value) {
// Parse value //Parse value
if (ty == TokenParser.TokenType.UnaryOperator) { if (ty == TokenParser.TokenType.UnaryOperator) {
//Add unary operator to the stack
parseOps.push(new ParseOperatorUnary(closeExpect, lastUnary)); parseOps.push(new ParseOperatorUnary(closeExpect, lastUnary));
closeExpect = -1; closeExpect = -1;
} else if (ty == TokenParser.TokenType.GroupOperator) { } else if (ty == TokenParser.TokenType.GroupOperator) {
if (lastGroup.end) { if (lastGroup.end) {
throw new AssemblyException("Unexpected group close", line); throw new AssemblyException("Unexpected group close", line);
} }
//No need to push a pointless operator.
if (closeExpect != -1) { if (closeExpect != -1) {
parseOps.push(new ParseOperator(closeExpect)); parseOps.push(new ParseOperator(closeExpect));
} }
closeExpect = lastGroup.groupType; closeExpect = lastGroup.groupType;
} else if (ty == TokenParser.TokenType.Constant) { } else if (ty == TokenParser.TokenType.Constant) {
lastValue = lastInt; lastValue = lastInt;
//Look for binary operators next
context = TokenParser.ParseContext.TackOn; context = TokenParser.ParseContext.TackOn;
} else { } else {
throw new AssemblyException("Value not found", line); throw new AssemblyException("Value not found", line);
} }
} else { } else {
// Parse modifier //Parse modifier
if (ty == TokenParser.TokenType.EOF || ty == TokenParser.TokenType.GroupOperator) { if (ty == TokenParser.TokenType.EOF || ty == TokenParser.TokenType.GroupOperator) {
if (ty == TokenParser.TokenType.GroupOperator && !lastGroup.end) { if (ty == TokenParser.TokenType.GroupOperator && !lastGroup.end) {
throw new AssemblyException("Unexpected group open", line); throw new AssemblyException("Unexpected group open", line);
} }
if (closeExpect != -1) { if (closeExpect != -1) {
if (ty == TokenParser.TokenType.EOF) { if (ty == TokenParser.TokenType.EOF) {
throw new AssemblyException("Unclosed group", line); throw new AssemblyException("Unclosed group", line);
} else if (closeExpect != lastGroup.groupType) { } else if (closeExpect != lastGroup.groupType) {
throw new AssemblyException("Unmatched group ends", line); throw new AssemblyException("Unmatched group ends", line);
} else { } else {
//New group operator closes the stored group operator
closeExpect = -1; closeExpect = -1;
continue; continue;
} }
} }
boolean completed = false; boolean completed = false;
//Evaluation chain, repeats as long as there are operators that do not require closing operators.
//Evaluation chain
while (!parseOps.isEmpty()) { while (!parseOps.isEmpty()) {
ParseOperator op = parseOps.pop(); ParseOperator op = parseOps.pop();
if (op.closeExpect != -1) { if (op.closeExpect != -1) {
@ -417,6 +442,7 @@ public class TokenParser {
} else if (op.closeExpect != lastGroup.groupType) { } else if (op.closeExpect != lastGroup.groupType) {
throw new AssemblyException("Unmatched group ends", line); throw new AssemblyException("Unmatched group ends", line);
} }
//Found matching operator, stop evaluating.
lastValue = op.apply(lastValue); lastValue = op.apply(lastValue);
completed = true; completed = true;
break; break;
@ -424,6 +450,7 @@ public class TokenParser {
lastValue = op.apply(lastValue); lastValue = op.apply(lastValue);
} }
if (!completed) { if (!completed) {
// There are no more operators
if (ty == TokenParser.TokenType.EOF) { if (ty == TokenParser.TokenType.EOF) {
return (char)lastValue; return (char)lastValue;
} else if (lastGroup.groupType != -1) { } else if (lastGroup.groupType != -1) {
@ -432,9 +459,12 @@ public class TokenParser {
} }
} else if (ty == TokenParser.TokenType.BinaryOperator) { } else if (ty == TokenParser.TokenType.BinaryOperator) {
TokenParser.BinaryOperatorType bop = lastBinary; 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()) { while (closeExpect == -1 && !parseOps.empty()) {
ParseOperator op = parseOps.peek(); ParseOperator op = parseOps.peek();
if (bop.precedence <= op.getPrecedence()) { if (bop.precedence < op.getPrecedence()) {
//The new operator is to be evaluated first.
break; break;
} }
lastValue = op.apply(lastValue); lastValue = op.apply(lastValue);
@ -445,7 +475,9 @@ public class TokenParser {
closeExpect = -1; closeExpect = -1;
context = TokenParser.ParseContext.Value; context = TokenParser.ParseContext.Value;
} }
else throw new AssemblyException("Modifier or end not found", line); else {
throw new AssemblyException("Modifier or end not found", line);
}
} }
} }
} }

View File

@ -141,6 +141,7 @@ public class TokenTest {
assertParse((char)-170, "(10 * -((9 + 8)))"); assertParse((char)-170, "(10 * -((9 + 8)))");
assertParse(170, "(-10 * -((9 + 8)))"); assertParse(170, "(-10 * -((9 + 8)))");
assertParse(2, "(-3 + 5)"); assertParse(2, "(-3 + 5)");
assertParse(3, "10 - 4 - 3");
failParse("1)"); failParse("1)");
failParse("(1"); failParse("(1");
@ -148,5 +149,10 @@ public class TokenTest {
failParse("1 + 1)"); failParse("1 + 1)");
failParse("((1 + 1)"); failParse("((1 + 1)");
failParse("(1 + 1))"); failParse("(1 + 1))");
failParse("+ 1");
failParse("(+ 1)");
failParse("(1 +)");
failParse("1 + ()");
failParse("() + 1");
} }
} }