z80-tools/src/nl/grauw/glass/Parser.java

667 wiersze
22 KiB
Java

package nl.grauw.glass;
import java.io.File;
import java.io.IOException;
import java.io.LineNumberReader;
import java.util.ArrayList;
import nl.grauw.glass.expressions.CharacterLiteral;
import nl.grauw.glass.expressions.ExpressionBuilder;
import nl.grauw.glass.expressions.Identifier;
import nl.grauw.glass.expressions.IntegerLiteral;
import nl.grauw.glass.expressions.StringLiteral;
public class Parser {
private Scope scope;
private LineBuilder lineBuilder = new LineBuilder();
private State state;
private StringBuilder accumulator = new StringBuilder();
private ExpressionBuilder expressionBuilder = new ExpressionBuilder();
public Line parse(LineNumberReader reader, Scope scope, File sourceFile) {
this.scope = scope;
state = labelStartState;
final int firstLineNumber = reader.getLineNumber();
int lineNumber = firstLineNumber;
int column = 0;
ArrayList<String> sourceLines = new ArrayList<String>();
try {
while (state != endState)
{
String sourceLine = reader.readLine();
if (sourceLine == null) {
state = state.parse('\0');
if (state != endState)
throw new AssemblyException("Unexpected end of file.");
if (sourceLines.size() > 0)
break; // return null (parsing end) the next time
return null;
}
sourceLines.add(sourceLine);
lineNumber = reader.getLineNumber();
column = 0;
for (int i = 0, length = sourceLine.length(); i < length; i++) {
column = i;
state = state.parse(sourceLine.charAt(i));
}
column = sourceLine.length();
state = state.parse('\n');
}
if (accumulator.length() > 0)
throw new AssemblyException("Accumulator not consumed. Value: " + accumulator.toString());
} catch(AssemblyException e) {
e.addContext(sourceFile, lineNumber, column, String.join("\n", sourceLines));
throw e;
} catch (IOException e) {
throw new AssemblyException(e);
}
lineBuilder.setSourceText(String.join("\n", sourceLines));
return lineBuilder.getLine(scope, sourceFile, firstLineNumber);
}
private abstract class State {
public abstract State parse(char character);
public boolean isWhitespace(char character) {
return character == ' ' || character == '\t';
}
public boolean isIdentifier(char character) {
return isIdentifierStart(character) || character >= '0' && character <= '9' ||
character == '\'' || character == '$';
}
public boolean isIdentifierStart(char character) {
return character >= 'a' && character <= 'z' || character >= 'A' && character <= 'Z' ||
character == '_' || character == '.' || character == '?' || character == '@';
}
}
private LabelStartState labelStartState = new LabelStartState();
private class LabelStartState extends State {
public State parse(char character) {
if (isIdentifierStart(character)) {
accumulator.append(character);
return labelReadState;
} else if (isWhitespace(character)) {
return statementStartState;
} else if (character == ';') {
return commentReadState;
} else if (character == '\n' || character == '\0') {
return endState;
}
throw new SyntaxError();
}
}
private LabelReadState labelReadState = new LabelReadState();
private class LabelReadState extends State {
public State parse(char character) {
if (isIdentifier(character)) {
accumulator.append(character);
return labelReadState;
} else {
lineBuilder.setLabel(accumulator.toString());
accumulator.setLength(0);
if (character == ':' || isWhitespace(character)) {
return statementStartState;
} else if (character == ';') {
return commentReadState;
} else if (character == '\n' || character == '\0') {
return endState;
}
}
throw new SyntaxError();
}
}
private StatementStartState statementStartState = new StatementStartState();
private class StatementStartState extends State {
public State parse(char character) {
if (isIdentifierStart(character)) {
accumulator.append(character);
return statementReadState;
} else if (isWhitespace(character)) {
return statementStartState;
} else if (character == ';') {
return commentReadState;
} else if (character == '\n' || character == '\0') {
return endState;
}
throw new SyntaxError();
}
}
private StatementReadState statementReadState = new StatementReadState();
private class StatementReadState extends State {
public State parse(char character) {
if (isIdentifier(character)) {
accumulator.append(character);
return statementReadState;
} if (character == ':') {
lineBuilder.setLabel(accumulator.toString());
accumulator.setLength(0);
return statementStartState;
} else {
lineBuilder.setMnemonic(accumulator.toString());
accumulator.setLength(0);
if (isWhitespace(character)) {
return argumentStartState;
} else if (character == ';') {
return commentReadState;
} else if (character == '\n' || character == '\0') {
return endState;
}
}
throw new SyntaxError();
}
}
private ArgumentStartState argumentStartState = new ArgumentStartState();
private class ArgumentStartState extends State {
public State parse(char character) {
if (character == ';') {
return commentReadState;
} else if (character == '\n' || character == '\0') {
return endState;
} else if (isWhitespace(character)) {
return argumentStartState;
} else {
return argumentValueState.parse(character);
}
}
}
private ArgumentValueState argumentValueState = new ArgumentValueState();
private class ArgumentValueState extends State {
public State parse(char character) {
if (isIdentifierStart(character)) {
accumulator.append(character);
return argumentIdentifierState;
} else if (character == '0') {
accumulator.append(character);
return argumentZeroState;
} else if (character >= '1' && character <= '9') {
accumulator.append(character);
return argumentNumberState;
} else if (character == '#') {
return argumentHexadecimalState;
} else if (character == '$') {
return argumentDollarState;
} else if (character == '%') {
return argumentBinaryState;
} else if (character == '"') {
return argumentStringState;
} else if (character == '\'') {
return argumentCharacterState;
} else if (character == '+') {
expressionBuilder.addOperatorToken(expressionBuilder.POSITIVE);
return argumentValueState;
} else if (character == '-') {
expressionBuilder.addOperatorToken(expressionBuilder.NEGATIVE);
return argumentValueState;
} else if (character == '~') {
expressionBuilder.addOperatorToken(expressionBuilder.COMPLEMENT);
return argumentValueState;
} else if (character == '!') {
expressionBuilder.addOperatorToken(expressionBuilder.NOT);
return argumentValueState;
} else if (character == '(') {
expressionBuilder.addOperatorToken(expressionBuilder.GROUP_OPEN);
return argumentValueState;
} else if (isWhitespace(character)) {
return argumentValueState;
} else if (character == ';') {
return commentReadThenArgumentState;
} else if (character == '\n') {
return argumentValueState;
}
throw new SyntaxError();
}
}
private ArgumentIdentifierState argumentIdentifierState = new ArgumentIdentifierState();
private class ArgumentIdentifierState extends State {
public State parse(char character) {
if (isIdentifier(character)) {
accumulator.append(character);
return argumentIdentifierState;
} else {
expressionBuilder.addValueToken(new Identifier(accumulator.toString(), scope));
accumulator.setLength(0);
return argumentOperatorState.parse(character);
}
}
}
private ArgumentStringState argumentStringState = new ArgumentStringState();
private class ArgumentStringState extends State {
public State parse(char character) {
if (character == '"') {
expressionBuilder.addValueToken(new StringLiteral(accumulator.toString()));
accumulator.setLength(0);
return argumentOperatorState;
} else if (character == '\\') {
return argumentStringEscapeState;
} else if (character == '\n' || character == '\0') {
throw new SyntaxError();
} else {
accumulator.append(character);
return argumentStringState;
}
}
}
private ArgumentStringEscapeState argumentStringEscapeState = new ArgumentStringEscapeState();
private class ArgumentStringEscapeState extends State {
public State parse(char character) {
if (character == '0') {
accumulator.append('\0');
return argumentStringState;
} else if (character == 'a') {
accumulator.append('\7');
return argumentStringState;
} else if (character == 't') {
accumulator.append('\t');
return argumentStringState;
} else if (character == 'n') {
accumulator.append('\n');
return argumentStringState;
} else if (character == 'f') {
accumulator.append('\f');
return argumentStringState;
} else if (character == 'r') {
accumulator.append('\r');
return argumentStringState;
} else if (character == 'e') {
accumulator.append('\33');
return argumentStringState;
} else if (character == '"') {
accumulator.append('"');
return argumentStringState;
} else if (character == '\'') {
accumulator.append('\'');
return argumentStringState;
} else if (character == '\\') {
accumulator.append('\\');
return argumentStringState;
} else if (character == '\n' || character == '\0') {
throw new SyntaxError();
} else {
throw new SyntaxError();
}
}
}
private ArgumentCharacterState argumentCharacterState = new ArgumentCharacterState();
private class ArgumentCharacterState extends State {
public State parse(char character) {
if (character == '\\') {
return argumentCharacterEscapeState;
} else if (character == '\'' || character == '\n' || character == '\0') {
throw new SyntaxError();
} else {
accumulator.append(character);
return argumentCharacterEndState;
}
}
}
private ArgumentCharacterEscapeState argumentCharacterEscapeState = new ArgumentCharacterEscapeState();
private class ArgumentCharacterEscapeState extends State {
public State parse(char character) {
State state = argumentStringEscapeState.parse(character);
if (state == argumentStringState)
return argumentCharacterEndState;
throw new AssemblyException("Unexpected state.");
}
}
private ArgumentCharacterEndState argumentCharacterEndState = new ArgumentCharacterEndState();
private class ArgumentCharacterEndState extends State {
public State parse(char character) {
if (character == '\'') {
expressionBuilder.addValueToken(new CharacterLiteral(accumulator.charAt(0)));
accumulator.setLength(0);
return argumentOperatorState;
} else {
throw new SyntaxError();
}
}
}
private ArgumentZeroState argumentZeroState = new ArgumentZeroState();
private class ArgumentZeroState extends State {
public State parse(char character) {
if (character == 'x' || character == 'X') {
accumulator.setLength(0);
return argumentHexadecimalState;
} else {
return argumentNumberState.parse(character);
}
}
}
private ArgumentNumberState argumentNumberState = new ArgumentNumberState();
private class ArgumentNumberState extends State {
public State parse(char character) {
if (character >= '0' && character <= '9' || character >= 'A' && character <= 'F' ||
character >= 'a' && character <= 'f') {
accumulator.append(character);
return argumentNumberState;
} else {
String string = accumulator.toString();
if (character == 'H' || character == 'h') {
int value = parseInt(string, 16);
expressionBuilder.addValueToken(new IntegerLiteral(value));
accumulator.setLength(0);
return argumentOperatorState;
} else if (character == 'O' || character == 'o') {
int value = parseInt(string, 8);
expressionBuilder.addValueToken(new IntegerLiteral(value));
accumulator.setLength(0);
return argumentOperatorState;
} else {
if (string.endsWith("B") || string.endsWith("b")) {
int value = parseInt(string.substring(0, string.length() - 1), 2);
expressionBuilder.addValueToken(new IntegerLiteral(value));
accumulator.setLength(0);
} else {
int value = parseInt(string, 10);
expressionBuilder.addValueToken(new IntegerLiteral(value));
accumulator.setLength(0);
}
return argumentOperatorState.parse(character);
}
}
}
}
private ArgumentDollarState argumentDollarState = new ArgumentDollarState();
private class ArgumentDollarState extends State {
public State parse(char character) {
if (character >= '0' && character <= '9' || character >= 'A' && character <= 'F' ||
character >= 'a' && character <= 'f') {
accumulator.append(character);
return argumentHexadecimalState;
} else {
expressionBuilder.addValueToken(new Identifier("$", scope));
accumulator.setLength(0);
return argumentOperatorState.parse(character);
}
}
}
private ArgumentHexadecimalState argumentHexadecimalState = new ArgumentHexadecimalState();
private class ArgumentHexadecimalState extends State {
public State parse(char character) {
if (character >= '0' && character <= '9' || character >= 'A' && character <= 'F' ||
character >= 'a' && character <= 'f') {
accumulator.append(character);
return argumentHexadecimalState;
} else {
int value = parseInt(accumulator.toString(), 16);
expressionBuilder.addValueToken(new IntegerLiteral(value));
accumulator.setLength(0);
return argumentOperatorState.parse(character);
}
}
}
private ArgumentBinaryState argumentBinaryState = new ArgumentBinaryState();
private class ArgumentBinaryState extends State {
public State parse(char character) {
if (character >= '0' && character <= '1') {
accumulator.append(character);
return argumentBinaryState;
} else {
int value = parseInt(accumulator.toString(), 2);
expressionBuilder.addValueToken(new IntegerLiteral(value));
accumulator.setLength(0);
return argumentOperatorState.parse(character);
}
}
}
private ArgumentOperatorState argumentOperatorState = new ArgumentOperatorState();
private class ArgumentOperatorState extends State {
public State parse(char character) {
if (character == ')') {
expressionBuilder.addOperatorToken(expressionBuilder.GROUP_CLOSE);
return argumentOperatorState;
} else if (character == '[') {
expressionBuilder.addOperatorToken(expressionBuilder.INDEX_OPEN);
return argumentValueState;
} else if (character == ']') {
expressionBuilder.addOperatorToken(expressionBuilder.INDEX_CLOSE);
return argumentOperatorState;
} else if (character == '.') {
expressionBuilder.addOperatorToken(expressionBuilder.MEMBER);
return argumentValueState;
} else if (character == '*') {
expressionBuilder.addOperatorToken(expressionBuilder.MULTIPLY);
return argumentValueState;
} else if (character == '/') {
expressionBuilder.addOperatorToken(expressionBuilder.DIVIDE);
return argumentValueState;
} else if (character == '%') {
expressionBuilder.addOperatorToken(expressionBuilder.MODULO);
return argumentValueState;
} else if (character == '+') {
expressionBuilder.addOperatorToken(expressionBuilder.ADD);
return argumentValueState;
} else if (character == '-') {
expressionBuilder.addOperatorToken(expressionBuilder.SUBTRACT);
return argumentValueState;
} else if (character == '<') {
return argumentLessThanState;
} else if (character == '>') {
return argumentGreaterThanState;
} else if (character == '=') {
expressionBuilder.addOperatorToken(expressionBuilder.EQUALS);
return argumentValueState;
} else if (character == '!') {
return argumentNotEqualsState;
} else if (character == '&') {
return argumentAndState;
} else if (character == '^') {
expressionBuilder.addOperatorToken(expressionBuilder.XOR);
return argumentValueState;
} else if (character == '|') {
return argumentOrState;
} else if (character == '?') {
expressionBuilder.addOperatorToken(expressionBuilder.TERNARYIF);
return argumentValueState;
} else if (character == ':') {
expressionBuilder.addOperatorToken(expressionBuilder.TERNARYELSE);
return argumentValueState;
} else if (character == ',') {
expressionBuilder.addOperatorToken(expressionBuilder.SEQUENCE);
return argumentValueState;
} else if (isWhitespace(character)) {
return argumentOperatorState;
} else if (character == ';') {
if (!expressionBuilder.hasOpenGroup()) {
lineBuilder.setArguments(expressionBuilder.getExpression());
return commentReadState;
} else {
return commentReadThenOperatorState;
}
} else if (character == '\n' || character == '\0') {
if (!expressionBuilder.hasOpenGroup() || character == '\0') {
lineBuilder.setArguments(expressionBuilder.getExpression());
return endState;
} else {
return argumentOperatorState;
}
} else {
expressionBuilder.addOperatorToken(expressionBuilder.ANNOTATION);
return argumentValueState.parse(character);
}
}
}
private ArgumentLessThanState argumentLessThanState = new ArgumentLessThanState();
private class ArgumentLessThanState extends State {
public State parse(char character) {
if (character == '<') {
expressionBuilder.addOperatorToken(expressionBuilder.SHIFT_LEFT);
return argumentValueState;
} else if (character == '=') {
expressionBuilder.addOperatorToken(expressionBuilder.LESS_OR_EQUALS);
return argumentValueState;
} else {
expressionBuilder.addOperatorToken(expressionBuilder.LESS_THAN);
return argumentValueState.parse(character);
}
}
}
private ArgumentGreaterThanState argumentGreaterThanState = new ArgumentGreaterThanState();
private class ArgumentGreaterThanState extends State {
public State parse(char character) {
if (character == '>') {
expressionBuilder.addOperatorToken(expressionBuilder.SHIFT_RIGHT);
return argumentValueState;
} else if (character == '=') {
expressionBuilder.addOperatorToken(expressionBuilder.GREATER_OR_EQUALS);
return argumentValueState;
} else {
expressionBuilder.addOperatorToken(expressionBuilder.GREATER_THAN);
return argumentValueState.parse(character);
}
}
}
private ArgumentNotEqualsState argumentNotEqualsState = new ArgumentNotEqualsState();
private class ArgumentNotEqualsState extends State {
public State parse(char character) {
if (character == '=') {
expressionBuilder.addOperatorToken(expressionBuilder.NOT_EQUALS);
return argumentValueState;
} else {
expressionBuilder.addOperatorToken(expressionBuilder.ANNOTATION);
expressionBuilder.addOperatorToken(expressionBuilder.NOT);
return argumentValueState.parse(character);
}
}
}
private ArgumentAndState argumentAndState = new ArgumentAndState();
private class ArgumentAndState extends State {
public State parse(char character) {
if (character == '&') {
expressionBuilder.addOperatorToken(expressionBuilder.LOGICAL_AND);
return argumentValueState;
} else {
expressionBuilder.addOperatorToken(expressionBuilder.AND);
return argumentValueState.parse(character);
}
}
}
private ArgumentOrState argumentOrState = new ArgumentOrState();
private class ArgumentOrState extends State {
public State parse(char character) {
if (character == '|') {
expressionBuilder.addOperatorToken(expressionBuilder.LOGICAL_OR);
return argumentValueState;
} else {
expressionBuilder.addOperatorToken(expressionBuilder.OR);
return argumentValueState.parse(character);
}
}
}
private CommentReadState commentReadState = new CommentReadState();
private class CommentReadState extends State {
public State parse(char character) {
if (character == '\n' || character == '\0') {
lineBuilder.setComment(accumulator.toString());
accumulator.setLength(0);
return endState;
} else {
accumulator.append(character);
return commentReadState;
}
}
}
private CommentReadThenArgumentState commentReadThenArgumentState = new CommentReadThenArgumentState();
private class CommentReadThenArgumentState extends State {
public State parse(char character) {
if (character == '\n' || character == '\0') {
lineBuilder.setComment(accumulator.toString());
accumulator.setLength(0);
if (character == '\0') {
throw new SyntaxError();
} else {
return argumentValueState;
}
} else {
accumulator.append(character);
return commentReadState;
}
}
}
private CommentReadThenOperatorState commentReadThenOperatorState = new CommentReadThenOperatorState();
private class CommentReadThenOperatorState extends State {
public State parse(char character) {
if (character == '\n' || character == '\0') {
lineBuilder.setComment(accumulator.toString());
accumulator.setLength(0);
if (character == '\0') {
lineBuilder.setArguments(expressionBuilder.getExpression());
return endState;
} else {
return argumentOperatorState;
}
} else {
accumulator.append(character);
return commentReadState;
}
}
}
private EndState endState = new EndState();
private class EndState extends State {
public State parse(char character) {
throw new AssemblyException("End state reached but not all characters consumed.");
}
}
private static int parseInt(String string, int radix) {
try {
long value = Long.parseLong(string, radix);
if (value > 0xFFFFFFFFL)
throw new SyntaxError();
return (int)value;
} catch (NumberFormatException e) {
throw new SyntaxError();
}
}
public static class SyntaxError extends AssemblyException {
private static final long serialVersionUID = 1L;
public SyntaxError() {
this(null);
}
public SyntaxError(Throwable cause) {
super("Syntax error.", cause);
}
}
}