package mhfc.net.common.util.parsing; import java.nio.IntBuffer; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import java.util.Objects; import java.util.function.Supplier; import mhfc.net.common.util.CompositeException; import mhfc.net.common.util.parsing.syntax.IBasicSequence; import mhfc.net.common.util.parsing.syntax.literals.FunctionCallLiteral; import mhfc.net.common.util.parsing.syntax.literals.HolderLiteral; import mhfc.net.common.util.parsing.syntax.literals.IExpression; import mhfc.net.common.util.parsing.syntax.literals.IdentifierLiteral; import mhfc.net.common.util.parsing.syntax.operators.ArgumentContinuationOperator; import mhfc.net.common.util.parsing.syntax.operators.FunctionOperator; import mhfc.net.common.util.parsing.syntax.operators.IBinaryOperator; import mhfc.net.common.util.parsing.syntax.operators.IOperator; import mhfc.net.common.util.parsing.syntax.operators.MemberOperator; import mhfc.net.common.util.parsing.syntax.operators.Operators; import mhfc.net.common.util.parsing.syntax.tree.AST; import mhfc.net.common.util.parsing.syntax.tree.SyntaxBuilder; import mhfc.net.common.util.parsing.syntax.tree.UnarySyntaxBuilder.ElementType; import mhfc.net.common.util.parsing.valueholders.ValueHolders; import net.minecraft.command.SyntaxErrorException; public class ExpressionTranslator { private static class BracketOp implements IOperator<Void, IExpression> { private IExpression expression; public BracketOp(IExpression exp) { this.expression = Objects.requireNonNull(exp); } @Override public IExpression with(Void value) { return expression; } } private static class OpeningBracketOp implements IOperator<IExpression, BracketOp> { @Override public BracketOp with(IExpression value) { return new BracketOp(value); } } private static final Supplier<AST> TREE_BUILDER; private static final int VAL_EXPRESSION_ID; private static final int VAL_IDENTIFIER_ID; private static final int VAL_FUNCTIONCALL_ID; private static final int VAL_CLOSING_BRACKET_ID; private static final int OP_MEMBERACCESS_ID; private static final int OP_FUNCTIONCALL_ID; private static final int OP_ARGUMENTCONTINUE_ID; private static final int OP_OPENING_BRACKET_ID; private static final int OP_BRACKET_ID; private static final int OP_COMPLEMENT_ID; private static final int OP_NOT_ID; private static final int OP_MUL_ID; private static final int OP_DIV_ID; private static final int OP_MOD_ID; private static final int OP_ADD_ID; private static final int OP_SUB_ID; private static final int OP_LSHIFT_ID; private static final int OP_RSHIFT_ID; private static final int OP_URSHIFT_ID; private static final int OP_LESS_ID; private static final int OP_LESSEQ_ID; private static final int OP_GREATER_ID; private static final int OP_GREATEREQ_ID; private static final int OP_EQUAL_ID; private static final int OP_UNEQUAL_ID; private static final int OP_BITAND_ID; private static final int OP_BITOR_ID; private static final int OP_BITXOR_ID; private static final int OP_LOGICALAND_ID; private static final int OP_LOGICALOR_ID; static { SyntaxBuilder SYNTAX_BUILDER = new SyntaxBuilder(); VAL_EXPRESSION_ID = SYNTAX_BUILDER.registerTerminal(IExpression.class); VAL_IDENTIFIER_ID = SYNTAX_BUILDER.registerTerminal(IdentifierLiteral.class, VAL_EXPRESSION_ID); VAL_FUNCTIONCALL_ID = SYNTAX_BUILDER.registerTerminal(FunctionCallLiteral.class, VAL_EXPRESSION_ID); VAL_CLOSING_BRACKET_ID = SYNTAX_BUILDER.registerTerminal(Void.class); OP_MEMBERACCESS_ID = SYNTAX_BUILDER.registerBinaryOperator( MemberOperator.class, VAL_EXPRESSION_ID, VAL_IDENTIFIER_ID, VAL_EXPRESSION_ID, true); OP_FUNCTIONCALL_ID = SYNTAX_BUILDER.registerBinaryOperator( FunctionOperator.class, VAL_EXPRESSION_ID, VAL_EXPRESSION_ID, VAL_FUNCTIONCALL_ID, true); OP_ARGUMENTCONTINUE_ID = SYNTAX_BUILDER.registerBinaryOperator( ArgumentContinuationOperator.class, VAL_FUNCTIONCALL_ID, VAL_EXPRESSION_ID, VAL_FUNCTIONCALL_ID, true); OP_BRACKET_ID = SYNTAX_BUILDER.registerUnaryOperator( BracketOp.class, true, VAL_CLOSING_BRACKET_ID, ElementType.VALUE, VAL_EXPRESSION_ID); OP_OPENING_BRACKET_ID = SYNTAX_BUILDER.registerUnaryOperator( OpeningBracketOp.class, true, VAL_EXPRESSION_ID, ElementType.PREFIX_OP, OP_BRACKET_ID); OP_COMPLEMENT_ID = SYNTAX_BUILDER .registerUnaryOperator(IOperator.class, true, VAL_EXPRESSION_ID, ElementType.VALUE, VAL_EXPRESSION_ID); OP_NOT_ID = SYNTAX_BUILDER .registerUnaryOperator(IOperator.class, true, VAL_EXPRESSION_ID, ElementType.VALUE, VAL_EXPRESSION_ID); OP_MUL_ID = SYNTAX_BUILDER.registerBinaryOperator( IBinaryOperator.class, VAL_EXPRESSION_ID, VAL_EXPRESSION_ID, VAL_EXPRESSION_ID, true); OP_DIV_ID = SYNTAX_BUILDER.registerBinaryOperator( IBinaryOperator.class, VAL_EXPRESSION_ID, VAL_EXPRESSION_ID, VAL_EXPRESSION_ID, true); OP_MOD_ID = SYNTAX_BUILDER.registerBinaryOperator( IBinaryOperator.class, VAL_EXPRESSION_ID, VAL_EXPRESSION_ID, VAL_EXPRESSION_ID, true); OP_ADD_ID = SYNTAX_BUILDER.registerBinaryOperator( IBinaryOperator.class, VAL_EXPRESSION_ID, VAL_EXPRESSION_ID, VAL_EXPRESSION_ID, true); OP_SUB_ID = SYNTAX_BUILDER.registerBinaryOperator( IBinaryOperator.class, VAL_EXPRESSION_ID, VAL_EXPRESSION_ID, VAL_EXPRESSION_ID, true); OP_LSHIFT_ID = SYNTAX_BUILDER.registerBinaryOperator( IBinaryOperator.class, VAL_EXPRESSION_ID, VAL_EXPRESSION_ID, VAL_EXPRESSION_ID, true); OP_RSHIFT_ID = SYNTAX_BUILDER.registerBinaryOperator( IBinaryOperator.class, VAL_EXPRESSION_ID, VAL_EXPRESSION_ID, VAL_EXPRESSION_ID, true); OP_URSHIFT_ID = SYNTAX_BUILDER.registerBinaryOperator( IBinaryOperator.class, VAL_EXPRESSION_ID, VAL_EXPRESSION_ID, VAL_EXPRESSION_ID, true); OP_LESS_ID = SYNTAX_BUILDER.registerBinaryOperator( IBinaryOperator.class, VAL_EXPRESSION_ID, VAL_EXPRESSION_ID, VAL_EXPRESSION_ID, true); OP_LESSEQ_ID = SYNTAX_BUILDER.registerBinaryOperator( IBinaryOperator.class, VAL_EXPRESSION_ID, VAL_EXPRESSION_ID, VAL_EXPRESSION_ID, true); OP_GREATER_ID = SYNTAX_BUILDER.registerBinaryOperator( IBinaryOperator.class, VAL_EXPRESSION_ID, VAL_EXPRESSION_ID, VAL_EXPRESSION_ID, true); OP_GREATEREQ_ID = SYNTAX_BUILDER.registerBinaryOperator( IBinaryOperator.class, VAL_EXPRESSION_ID, VAL_EXPRESSION_ID, VAL_EXPRESSION_ID, true); OP_EQUAL_ID = SYNTAX_BUILDER.registerBinaryOperator( IBinaryOperator.class, VAL_EXPRESSION_ID, VAL_EXPRESSION_ID, VAL_EXPRESSION_ID, true); OP_UNEQUAL_ID = SYNTAX_BUILDER.registerBinaryOperator( IBinaryOperator.class, VAL_EXPRESSION_ID, VAL_EXPRESSION_ID, VAL_EXPRESSION_ID, true); OP_BITAND_ID = SYNTAX_BUILDER.registerBinaryOperator( IBinaryOperator.class, VAL_EXPRESSION_ID, VAL_EXPRESSION_ID, VAL_EXPRESSION_ID, true); OP_BITOR_ID = SYNTAX_BUILDER.registerBinaryOperator( IBinaryOperator.class, VAL_EXPRESSION_ID, VAL_EXPRESSION_ID, VAL_EXPRESSION_ID, true); OP_BITXOR_ID = SYNTAX_BUILDER.registerBinaryOperator( IBinaryOperator.class, VAL_EXPRESSION_ID, VAL_EXPRESSION_ID, VAL_EXPRESSION_ID, true); OP_LOGICALAND_ID = SYNTAX_BUILDER.registerBinaryOperator( IBinaryOperator.class, VAL_EXPRESSION_ID, VAL_EXPRESSION_ID, VAL_EXPRESSION_ID, true); OP_LOGICALOR_ID = SYNTAX_BUILDER.registerBinaryOperator( IBinaryOperator.class, VAL_EXPRESSION_ID, VAL_EXPRESSION_ID, VAL_EXPRESSION_ID, true); SYNTAX_BUILDER.declarePrecedence(OP_MEMBERACCESS_ID, OP_ARGUMENTCONTINUE_ID); SYNTAX_BUILDER.declarePrecedence(OP_ARGUMENTCONTINUE_ID, OP_FUNCTIONCALL_ID); SYNTAX_BUILDER.declarePrecedence(OP_FUNCTIONCALL_ID, OP_COMPLEMENT_ID); SYNTAX_BUILDER.declarePrecedence(OP_FUNCTIONCALL_ID, OP_NOT_ID); SYNTAX_BUILDER.declarePrecedence(OP_FUNCTIONCALL_ID, OP_MUL_ID); SYNTAX_BUILDER.declarePrecedence(OP_FUNCTIONCALL_ID, OP_DIV_ID); SYNTAX_BUILDER.declarePrecedence(OP_FUNCTIONCALL_ID, OP_MOD_ID); SYNTAX_BUILDER.declarePrecedence(OP_COMPLEMENT_ID, OP_MUL_ID); SYNTAX_BUILDER.declarePrecedence(OP_NOT_ID, OP_MUL_ID); SYNTAX_BUILDER.declarePrecedence(OP_COMPLEMENT_ID, OP_DIV_ID); SYNTAX_BUILDER.declarePrecedence(OP_NOT_ID, OP_DIV_ID); SYNTAX_BUILDER.declarePrecedence(OP_COMPLEMENT_ID, OP_MOD_ID); SYNTAX_BUILDER.declarePrecedence(OP_NOT_ID, OP_MOD_ID); SYNTAX_BUILDER.declareSamePrecedence(OP_MUL_ID, OP_DIV_ID, true); SYNTAX_BUILDER.declareSamePrecedence(OP_MUL_ID, OP_MOD_ID, true); SYNTAX_BUILDER.declareSamePrecedence(OP_DIV_ID, OP_MOD_ID, true); SYNTAX_BUILDER.declarePrecedence(OP_MUL_ID, OP_ADD_ID); SYNTAX_BUILDER.declarePrecedence(OP_DIV_ID, OP_ADD_ID); SYNTAX_BUILDER.declarePrecedence(OP_MOD_ID, OP_ADD_ID); SYNTAX_BUILDER.declarePrecedence(OP_MUL_ID, OP_SUB_ID); SYNTAX_BUILDER.declarePrecedence(OP_DIV_ID, OP_SUB_ID); SYNTAX_BUILDER.declarePrecedence(OP_MOD_ID, OP_SUB_ID); SYNTAX_BUILDER.declareSamePrecedence(OP_ADD_ID, OP_SUB_ID, true); SYNTAX_BUILDER.declarePrecedence(OP_ADD_ID, OP_LSHIFT_ID); SYNTAX_BUILDER.declarePrecedence(OP_SUB_ID, OP_LSHIFT_ID); SYNTAX_BUILDER.declarePrecedence(OP_LSHIFT_ID, OP_RSHIFT_ID); SYNTAX_BUILDER.declarePrecedence(OP_RSHIFT_ID, OP_URSHIFT_ID); SYNTAX_BUILDER.declarePrecedence(OP_URSHIFT_ID, OP_LESS_ID); SYNTAX_BUILDER.declarePrecedence(OP_LESS_ID, OP_LESSEQ_ID); SYNTAX_BUILDER.declarePrecedence(OP_LESSEQ_ID, OP_GREATER_ID); SYNTAX_BUILDER.declarePrecedence(OP_GREATER_ID, OP_GREATEREQ_ID); SYNTAX_BUILDER.declarePrecedence(OP_GREATEREQ_ID, OP_EQUAL_ID); SYNTAX_BUILDER.declarePrecedence(OP_EQUAL_ID, OP_UNEQUAL_ID); SYNTAX_BUILDER.declarePrecedence(OP_UNEQUAL_ID, OP_BITAND_ID); SYNTAX_BUILDER.declarePrecedence(OP_BITAND_ID, OP_BITXOR_ID); SYNTAX_BUILDER.declarePrecedence(OP_BITXOR_ID, OP_BITOR_ID); SYNTAX_BUILDER.declarePrecedence(OP_BITOR_ID, OP_LOGICALAND_ID); SYNTAX_BUILDER.declarePrecedence(OP_LOGICALAND_ID, OP_LOGICALOR_ID); SYNTAX_BUILDER.declarePrecedence(OP_LOGICALOR_ID, OP_OPENING_BRACKET_ID); SYNTAX_BUILDER.validate(); AST mold = SYNTAX_BUILDER.newParseTree(); TREE_BUILDER = mold::makeFreshTree; } private static abstract class StringSequence implements IBasicSequence { private final int[] codepoints; private int currentPoint; public StringSequence(int codepoint) { this.codepoints = new int[] { codepoint }; } public StringSequence(String string) { if (Objects.requireNonNull(string).isEmpty()) { throw new IllegalArgumentException("string must not be empty"); } this.codepoints = string.codePoints().toArray(); assert this.codepoints.length > 0; } @Override public void reset() { currentPoint = 0; } @Override public SiftResult endOfStream() { return SiftResult.REJCECTED; } @Override public SiftResult accepting(int cp) { if (codepoints[currentPoint] != cp) { return SiftResult.REJCECTED; } currentPoint++; return currentPoint == codepoints.length ? SiftResult.FINISHED : SiftResult.ACCEPTED; } }; private static class Identifier implements IBasicSequence { private StringBuilder sb = new StringBuilder(); @Override public SiftResult accepting(int cp) { if (sb.length() == 0) { if (Character.isJavaIdentifierStart(cp)) { sb.appendCodePoint(cp); return SiftResult.ACCEPTED; } return SiftResult.REJCECTED; } if (Character.isJavaIdentifierPart(cp)) { sb.appendCodePoint(cp); return SiftResult.ACCEPTED; } return SiftResult.PAST_END; } @Override public void reset() { sb.setLength(0); } @Override public SiftResult endOfStream() { return this.sb.length() == 0 ? SiftResult.REJCECTED : SiftResult.PAST_END; } @Override public void pushOnto(AST ast) { ast.pushValue(VAL_IDENTIFIER_ID, new IdentifierLiteral(sb.toString())); } } private static class Whitespace implements IBasicSequence { @Override public SiftResult accepting(int cp) { if (Character.isWhitespace(cp)) { return SiftResult.FINISHED; } return SiftResult.REJCECTED; } @Override public void reset() {} @Override public SiftResult endOfStream() { return SiftResult.PAST_END; } @Override public void pushOnto(AST ast) { /* ignore */ } } private static class Comment implements IBasicSequence { private static enum State { BEFORE, FIRST_STAR, DURING, SECOND_STAR; } private State state = State.BEFORE; @Override public SiftResult accepting(int cp) { switch (state) { case BEFORE: state = State.FIRST_STAR; return cp == '/' ? SiftResult.ACCEPTED : SiftResult.REJCECTED; case FIRST_STAR: state = State.DURING; return cp == '*' ? SiftResult.ACCEPTED : SiftResult.REJCECTED; case DURING: if (cp == '*') { state = State.SECOND_STAR; } return SiftResult.ACCEPTED; case SECOND_STAR: if (cp == '/') { return SiftResult.FINISHED; } if (cp != '*') { state = State.DURING; } return SiftResult.ACCEPTED; default: throw new IllegalStateException("unreachable"); } } @Override public void reset() { state = State.BEFORE; } @Override public SiftResult endOfStream() { return SiftResult.REJCECTED; } @Override public void pushOnto(AST ast) { /* ignore */ } } private static class StringConstant implements IBasicSequence { private static enum State { BEGIN, ACTIVE; } private StringBuilder string = new StringBuilder(); private State state = State.BEGIN; private boolean escaped = false; private List<Throwable> errors = new ArrayList<>(); private int encountered = 0; @Override public SiftResult accepting(int cp) { if (state == State.BEGIN) { if (cp == '\"') { this.state = State.ACTIVE; this.encountered++; return SiftResult.ACCEPTED; } return SiftResult.REJCECTED; } if (!escaped) { if (cp == '\"') { return SiftResult.FINISHED; } if (cp == '\\') { escaped = true; return SiftResult.ACCEPTED; } string.appendCodePoint(cp); this.encountered++; return SiftResult.ACCEPTED; } switch (cp) { // \b \t \n \f \r \" \' \\ case 'b': string.appendCodePoint('\b'); break; case 't': string.appendCodePoint('\t'); break; case 'n': string.appendCodePoint('\n'); break; case 'f': string.appendCodePoint('\f'); break; case 'r': string.appendCodePoint('\r'); break; case '"': string.appendCodePoint('\"'); break; case '\'': string.appendCodePoint('\''); break; case '\\': string.appendCodePoint('\\'); break; default: { // Illegal escape sequence String error = "Illegal escape sequence \\" + String.valueOf(Character.toChars(cp)) + " at index " + this.encountered; this.encountered++; this.errors.add(new IllegalArgumentException(error)); } } this.escaped = false; return SiftResult.ACCEPTED; } @Override public void reset() { state = State.BEGIN; escaped = false; string.setLength(0); errors.clear(); encountered = 0; } @Override public SiftResult endOfStream() { return SiftResult.REJCECTED; } @Override public void pushOnto(AST ast) { IValueHolder value; if (this.errors.size() == 0) { value = Holder.valueOrEmpty(this.string.toString()); } else { value = ValueHolders.throwing(() -> new CompositeException(this.errors)); } ast.pushValue(VAL_EXPRESSION_ID, new HolderLiteral(value)); } } private static class IntegerConstant implements IBasicSequence { private static enum State { START, POSTSIGN, NUMBER; } private int base; private int length; private State state; private StringBuilder string = new StringBuilder(); @Override public SiftResult accepting(int cp) { if (state == State.START) { if (cp == '+' || cp == '-') { string.appendCodePoint(cp); state = State.POSTSIGN; return SiftResult.ACCEPTED; } } if (state == State.START || state == State.POSTSIGN) { if (cp == '0') { base = 0; return SiftResult.ACCEPTED; } if (cp == 'o' && base == 0) { base = 8; state = State.NUMBER; return SiftResult.ACCEPTED; } if (cp == 'x' && base == 0) { base = 16; state = State.NUMBER; return SiftResult.ACCEPTED; } if (cp == 'b' && base == 0) { base = 2; state = State.NUMBER; return SiftResult.ACCEPTED; } state = State.NUMBER; if (base == 0) { // Literal 0, followed... string.appendCodePoint('0'); base = 10; return SiftResult.PAST_END; } } if (state != State.NUMBER) { return SiftResult.REJCECTED; } if (Character.digit(cp, base) != -1) { length++; string.appendCodePoint(cp); return SiftResult.ACCEPTED; } if (length > 0) { return SiftResult.PAST_END; } return SiftResult.REJCECTED; } @Override public SiftResult endOfStream() { return length > 0 ? SiftResult.PAST_END : SiftResult.REJCECTED; } @Override public void reset() { base = 10; length = 0; state = State.START; string.setLength(0); } @Override public void pushOnto(AST ast) { IValueHolder value; try { int val = Integer.parseInt(string.toString(), base); value = Holder.valueOf(val); } catch (NumberFormatException nfe) { value = ValueHolders.throwing(() -> nfe); } ast.pushValue(VAL_EXPRESSION_ID, new HolderLiteral(value)); } } private static IBasicSequence makeUnaryOperator(int matchingChar, int ID, Supplier<IOperator<?, ?>> opSupplier) { return new StringSequence(matchingChar) { @Override public void pushOnto(AST ast) throws SyntaxErrorException { ast.pushUnaryOperator(ID, opSupplier.get()); } }; } private static IBasicSequence makeBinaryOperator( int matchingChar, int ID, Supplier<IBinaryOperator<?, ?, ?>> opSupplier) { return new StringSequence(matchingChar) { @Override public void pushOnto(AST ast) throws SyntaxErrorException { ast.pushBinaryOperator(ID, opSupplier.get()); } }; } private static IBasicSequence makeBinaryOperator( String matching, int ID, Supplier<IBinaryOperator<?, ?, ?>> opSupplier) { return new StringSequence(matching) { @Override public void pushOnto(AST ast) throws SyntaxErrorException { ast.pushBinaryOperator(ID, opSupplier.get()); } }; } private static class OpeningBracket extends StringSequence { public OpeningBracket() { super('('); } @Override public void pushOnto(AST ast) throws SyntaxErrorException { ast.pushUnaryOperator(OP_OPENING_BRACKET_ID, new OpeningBracketOp()); } } private static class ClosingBracket extends StringSequence { public ClosingBracket() { super(')'); } @Override public void pushOnto(AST ast) throws SyntaxErrorException { ast.pushValue(VAL_CLOSING_BRACKET_ID, null); } } private static class ContextSymbol extends StringSequence { public ContextSymbol() { super('$'); } @Override public void pushOnto(AST ast) throws SyntaxErrorException { ast.pushValue(VAL_EXPRESSION_ID, new HolderLiteral(c -> c, "<context>")); } } private final List<IBasicSequence> sequences = new ArrayList<>(); public ExpressionTranslator() { sequences.add(new Whitespace()); sequences.add(new Comment()); sequences.add(makeBinaryOperator('.', OP_MEMBERACCESS_ID, MemberOperator::new)); sequences.add(makeBinaryOperator('|', OP_FUNCTIONCALL_ID, FunctionOperator::new)); sequences.add(makeBinaryOperator(':', OP_ARGUMENTCONTINUE_ID, ArgumentContinuationOperator::new)); sequences.add(makeBinaryOperator("==", OP_EQUAL_ID, Operators::makeEqualOp)); sequences.add(makeBinaryOperator("!=", OP_UNEQUAL_ID, Operators::makeNotEqualOp)); sequences.add(makeBinaryOperator("&&", OP_LOGICALAND_ID, Operators::makeConditionalAndOp)); sequences.add(makeBinaryOperator("||", OP_LOGICALOR_ID, Operators::makeConditionalOrOp)); sequences.add(makeBinaryOperator('*', OP_MUL_ID, Operators::makeMultiplyOp)); sequences.add(makeBinaryOperator('/', OP_DIV_ID, Operators::makeDivideOp)); sequences.add(makeBinaryOperator('%', OP_MOD_ID, Operators::makeModuloOp)); sequences.add(makeBinaryOperator("<<", OP_LSHIFT_ID, Operators::makeLeftShiftOp)); sequences.add(makeBinaryOperator(">>", OP_RSHIFT_ID, Operators::makeRightShiftOp)); sequences.add(makeBinaryOperator(">>>", OP_URSHIFT_ID, Operators::makeUnsignedRightShiftOp)); sequences.add(makeBinaryOperator("<=", OP_LESSEQ_ID, Operators::makeLessOrEqOp)); sequences.add(makeBinaryOperator(">=", OP_GREATEREQ_ID, Operators::makeGreaterOrEqualOp)); sequences.add(makeBinaryOperator("<", OP_LESS_ID, Operators::makeLessThanOp)); sequences.add(makeBinaryOperator(">", OP_GREATER_ID, Operators::makeGreaterThanOp)); sequences.add(makeUnaryOperator('!', OP_NOT_ID, Operators::makeLogicalComplementOp)); sequences.add(makeUnaryOperator('~', OP_COMPLEMENT_ID, Operators::makeBitwiseComplementOp)); sequences.add(makeBinaryOperator('+', OP_ADD_ID, Operators::makeAddOp)); sequences.add(makeBinaryOperator('-', OP_SUB_ID, Operators::makeMinusOp)); sequences.add(makeBinaryOperator('&', OP_BITAND_ID, Operators::makeAndOp)); sequences.add(makeBinaryOperator('^', OP_BITXOR_ID, Operators::makeXorOp)); sequences.add(makeBinaryOperator('|', OP_BITOR_ID, Operators::makeOrOp)); sequences.add(new ContextSymbol()); sequences.add(new OpeningBracket()); sequences.add(new ClosingBracket()); sequences.add(new Identifier()); sequences.add(new StringConstant()); sequences.add(new IntegerConstant()); } public IValueHolder parse(String expression, IValueHolder contextValue) throws SyntaxErrorException { // Replace all comments // String noComment = commentPattern.matcher(expression).replaceAll(" "); return parseCleaned(expression, contextValue); } private IValueHolder parseCleaned(String cleanExpression, IValueHolder contextValue) throws SyntaxErrorException { // Cleaned as in: does only contain spaces as whitespace, doesn't // contain any comments and only one sequential whitespace. // So we can reset AST parseTree = TREE_BUILDER.get(); IntBuffer expressionBuf = IntBuffer.wrap(cleanExpression.codePoints().toArray()); expressionBuf.mark(); Iterator<IBasicSequence> basicSequences = sequences.iterator(); IBasicSequence currentSequence = nextSequenceOrSyntaxError(basicSequences, expressionBuf); parse_loop: while (true) { if (!expressionBuf.hasRemaining()) { switch (currentSequence.endOfStream()) { case ACCEPTED: case FINISHED: throw new SyntaxErrorException("Can't accept end-of-stream, only PAST_END or REJECTED"); case PAST_END: currentSequence.pushOnto(parseTree); break parse_loop; case REJCECTED: default: currentSequence.reset(); expressionBuf.reset(); currentSequence = nextSequenceOrSyntaxError(basicSequences, expressionBuf); break; } continue; } int cp = expressionBuf.get(); switch (currentSequence.accepting(cp)) { case ACCEPTED: break; case PAST_END: // Rewind by 1 expressionBuf.position(expressionBuf.position() - 1); /* no break */ case FINISHED: // Push the syntax stack expressionBuf.mark(); currentSequence.pushOnto(parseTree); basicSequences = sequences.iterator(); currentSequence = nextSequenceOrSyntaxError(basicSequences, expressionBuf); break; case REJCECTED: default: // Reset to the mark, try next sequence expressionBuf.reset(); currentSequence = nextSequenceOrSyntaxError(basicSequences, expressionBuf); break; } } return IExpression.class.cast(parseTree.getOverallValue()).asValue(contextValue); } private IBasicSequence nextSequenceOrSyntaxError(Iterator<IBasicSequence> sequences, IntBuffer stream) { if (sequences.hasNext()) { IBasicSequence next = sequences.next(); next.reset(); return next; } // Collect the next few integers int collected = Math.min(stream.remaining(), 20); int position = stream.position(); int[] out = new int[collected]; stream.get(out); String asString = new String(out, 0, collected); String message = "Encountered unexpected sequence: (first 20) \"" + asString + "\" at " + position; throw new SyntaxErrorException(message); } }