BlockStatement(Statement[] statements)
     : super(StatementKind.Block) {
 LabeledStatement(Label label, Statement statement)
     : super(StatementKind.Labeled) {
		private void statementToString(Statement statement, StringBuilder sb, String indent) {
			switch (statement.StatementKind) {
			case Block:
				sb.append(indent).append("{\n");
				foreach (var s in ((BlockStatement)statement).Statements) {
					statementToString(s, sb, indent + "    ");
				}
				sb.append(indent).append("}");
				break;

			case Break:
				sb.append(indent).append("break;");
				break;
				
			case Continue:
				sb.append(indent).append("continue;");
				break;
				
			case Do:
				var doStatement = (LoopStatement)statement;
				sb.append(indent).append("do\n");
				statementToString(doStatement.Body, sb, indent + "    ");
				sb.append(indent).append("while (");
				expressionToString(doStatement.Condition, sb, "");
				sb.append(");");
				break;

			case Empty:
				sb.append(";");
				break;
				
			case Goto:
				sb.append(indent).append("goto ");
				labelToString(((GotoStatement)statement).Label, sb);
				sb.append(";");
				break;

			case GotoCase: {
				var gotoCase = (GotoCaseStatement)statement;
				sb.append(indent).append("goto ");
				int n = switchLabels.indexOf(gotoCase.Label);
				if (n == -1) {
					n = switchLabels.size();
					switchLabels.add(gotoCase.Label);
				}
				if (gotoCase.Label.Default) {
					sb.append("default ").append("#").append(n).append("#");
				} else {
					sb.append("case ").append("#").append(n).append("#");
					if (gotoCase.Label.Name != null) {
						sb.append("\"").append(gotoCase.Label.Name).append("\"");
					} else {
						sb.append(gotoCase.Label.Value);
					}
				}
				
				sb.append(";");
				break;
			}
				
			case Expression:
				var expression = (ExpressionStatement)statement;
				expressionToString(expression.Expression, sb, indent);
				sb.append(";");
				break;
				
			case If:
				var ifStatement = (IfStatement)statement;
				sb.append(indent).append("if (");
				expressionToString(ifStatement.Condition, sb, "");
				sb.append(")\n");
				statementToString(ifStatement.IfTrue, sb, indent + "    ");
				if (ifStatement.IfFalse != null) {
					sb.append(indent).append("else\n");
					statementToString(ifStatement.IfFalse, sb, indent + "    ");
				}
				break;
				
			case Labeled:
				var labeled = (LabeledStatement)statement;
				sb.append(indent);
				labelToString(labeled.Label, sb);
				sb.append(":\n");
				statementToString(labeled.Statement, sb, indent);
				break;
				
			case Return:
				sb.append(indent).append("return ");
				expressionToString(((ReturnStatement)statement).Value, sb, "");
				sb.append(";");
				break;

			case Switch:
				var switchStatement = (SwitchStatement)statement;
				sb.append(indent).append("switch (");
				expressionToString(switchStatement.Expression, sb, "");
				sb.append(") {\n");
				foreach (var section in switchStatement.Sections) {
					foreach (var label in section.Labels) {
						int n = switchLabels.indexOf(label);
						if (n == -1) {
							n = switchLabels.size();
							switchLabels.add(label);
						}
						sb.append(indent);
						if (label.Default) {
							sb.append("#").append(n).append("#").append("default:\n");
						} else {
							sb.append("#").append(n).append("#").append("case ");
							if (label.Name != null) {
								sb.append("\"").append(label.Name).append("\"");
							} else {
								sb.append(label.Value);
							}
							sb.append(":\n");
						}
					}
					foreach (var s in section.Statements) {
						statementToString(s, sb, indent + "    ");
					}
				}
				sb.append(indent).append("}");
				break;
				
			case Synchronized:
				var synchronizedStatement = (SynchronizedStatement)statement;
				sb.append(indent).append("synchronized (");
				expressionToString(synchronizedStatement.Lock, sb, "");
				sb.append(")\n");
				statementToString(synchronizedStatement.Body, sb, indent + "    ");
				break;
				
			case Throw:
				sb.append(indent).append("throw ");
				expressionToString(((ThrowStatement)statement).Exception, sb, "");
				sb.append(";");
				break;
				
			case Try:
				var tryStatement = (TryStatement)statement;
				sb.append(indent).append("try\n");
				statementToString(tryStatement.Body, sb, indent);
				foreach (var c in tryStatement.CatchClauses) {
					sb.append(indent).append("catch (");
					variableToString(c.Variable, sb);
					sb.append(")\n");
					statementToString(c.Body, sb, indent);
				}
				if (tryStatement.Finally != null) {
					sb.append(indent).append("finally\n");
					statementToString(tryStatement.Finally, sb, indent);
				}
				break;
				
			case While:
				var whileStatement = (LoopStatement)statement;
				sb.append(indent).append("while (");
				expressionToString(whileStatement.Condition, sb, "");
				sb.append(")\n");
				statementToString(whileStatement.Body, sb, indent + "    ");
				break;
				
			default:
				throw new IllegalStateException("Unhandled statement " + statement.StatementKind);
			}
			sb.append("\n");
		}
 ForStatement(Statement[] initializer, Expression condition, Statement[] iterator, Statement body)
     : super(StatementKind.For) {
 LambdaExpression(Class<?> type, VariableExpression[] parameters, Statement body)
     : super(ExpressionKind.Lambda, type) {