4 September 2011

The labeled break in Java

Trying to optimize the Android app I'm working on and found this Java feature that I'm sure I knew at one point but ignored as useless. My mistake.

The code I'm working on is in the script parser. The parser has one class method per grammar rule (e.g. parser.processStatement() or parser.processExpression()) following general top-down parsing practice. Within each method/rule, the parser can fail on a token without being certain that the token won't satisfy a subsequent rule. For example: if processStatementLoop() is called before processStatementConditional() and an "if" is the first token encountered in the script, the token stack (and possibly other internal structures) needs to rewind after processStatementLoop() fails on "if". The parser needs to know that the grammar has not yet accepted the "if" as valid. So, a rule may fail on a valid script.

When the grammar was small, I started with various conditionals peppered through each method, unwinding the relevant structures if the rule failed. I changed the conditionals to exceptions because it was easier to manage multiple failures within a single rule. However, the exceptions could be thrown either because of an error in the script or because of simple backtracking in the grammar. The latter isn't a true exception, and so this could be considered misuse of the exception construct.

	private boolean processStatementLoop() {
		transaction.begin();
		try {
			if (!processToken(TOKEN_FOR, true)) {
				throw new ScriptException(tokenizer);
			}

			// ...

		} catch (ScriptException e) {
			transaction.rollback();
			return false;
		}
		transaction.end();
		return true;
	}

And so I pondered and searched on a substitute for the exception in this instance. I'm not sure how I found it, but the Java doc article Branching Statements described the mysterious labeled break. Using this, you can break out of a block a la the "beloved" retro GOTO statement:

	private boolean processStatementLoop() {
		transaction.begin();
		Transaction: {
			if (!processToken(TOKEN_FOR, true)) {
				break Transaction;
			}

			// ...

			transaction.end();
			return true;
		}
		transaction.rollback();
		return false;
	}

This (should) avoid the cost of exception construction and stack unwinding, but more importantly it more closely maps the code's intent.

[ posted by sstrader on 4 September 2011 at 11:27:56 AM in Programming ]