private void print(StatementNode statement, int indent, bool format, StringBuilder sb) {
			if (!format) {
				indent = 0;
			}
			var indentText = buildIndentText(indent);
			switch (statement.StatementKind) {
			case Empty:
				sb.append(indentText);
				sb.append(";\r\n");
				break;

			case Block:
				var block = (BlockStatementNode)statement;
				sb.append("{");
				if (format) {
					sb.append("\r\n");
				} else {
					sb.append(" ");
				}
				foreach (var s in block.getStatements()) {
					print(s, indent + 1, format, sb);
					if (!format) {
						sb.append("; ");
					}
				}
				sb.append(indentText);
				sb.append("}");
				if (format) {
					sb.append("\r\n");
				}
				break;

			case Expression:
				var expression = (ExpressionStatementNode)statement;
				sb.append(indentText);
				print(expression.Expression, sb);
				if (format) {
					sb.append(";\r\n");
				}
				break;

			case Return:
				var returnStatement = (ReturnStatementNode)statement;
				sb.append(indentText);
				sb.append("return");
				if (returnStatement.Value != null) {
					sb.append(" ");
					print(returnStatement.Value, sb);
				}
				if (format) {
					sb.append(";\r\n");
				}
				break;

			case Yield:
				var yieldStatement = (YieldStatementNode)statement;
				sb.append(indentText);
				sb.append("yield ");
				if (yieldStatement.Value == null) {
					sb.append("break");
				} else {
					sb.append("return ");
					print(yieldStatement.Value, sb);
				}
				if (format) {
					sb.append(";\r\n");
				}
				break;

			case LocalDeclaration:
				var local = (LocalDeclarationStatementNode)statement;
				sb.append(indentText);
				if (local.Type == null) {
					sb.append("var");
				} else {
					print(local.Type, sb);
				}
				sb.append(" ");
				bool first = true;
				foreach (var decl in local.Declarators) {
					if (first) {
						first = false;
					} else {
						sb.append(", ");
					}
					sb.append(new String(text, decl.NameOffset, decl.NameLength));
					if (decl.Value != null) {
						sb.append(" = ");
						print(decl.Value, sb);
					}
				}
				if (format) {
					sb.append(";\r\n");
				}
				break;

			case If:
				var ifStatement = (IfStatementNode)statement;
				sb.append(indentText);
				sb.append("if (");
				print(ifStatement.Condition, sb);
				sb.append(")");
				if (ifStatement.IfTrue.StatementKind != StatementKind.Block) {
					sb.append("\r\n");
					print(ifStatement.IfTrue, indent + 1, sb);
				} else {
					sb.append(" ");
					print(ifStatement.IfTrue, indent, sb);
				}
				if (ifStatement.IfFalse != null) {
					sb.append(indentText);
					sb.append("else");
					if (ifStatement.IfFalse.StatementKind != StatementKind.Block) {
						sb.append("\r\n");
						print(ifStatement.IfFalse, indent + 1, sb);
					} else {
						sb.append(" ");
						print(ifStatement.IfFalse, indent, sb);
					}
				}
				break;

			case Do:
				var doStatement = (DoStatementNode)statement;
				sb.append(indentText);
				sb.append("do");
				if (doStatement.Statement.StatementKind != StatementKind.Block) {
					sb.append("\r\n");
					print(doStatement.Statement, indent + 1, sb);
				} else {
					sb.append(" ");
					print(doStatement.Statement, indent, sb);
				}
				sb.append(indentText);
				sb.append("while(");
				print(doStatement.Condition, sb);
				sb.append(");\r\n");
				break;

			case Break:
				sb.append(indentText);
				sb.append("break");
				if (format) {
					sb.append(";\r\n");
				}
				break;

			case Continue:
				sb.append(indentText);
				sb.append("continue");
				if (format) {
					sb.append(";\r\n");
				}
				break;

			case Throw:
				var throwStatement = (ThrowStatementNode)statement;
				sb.append(indentText);
				sb.append("throw");
				if (throwStatement.Exception != null) {
					sb.append(" ");
					print(throwStatement.Exception, sb);
				}
				if (format) {
					sb.append(";\r\n");
				}
				break;

			case While:
				var whileStatement = (WhileStatementNode)statement;
				sb.append(indentText);
				sb.append("while(");
				print(whileStatement.Condition, sb);
				sb.append(")");
				if (whileStatement.Statement.StatementKind != StatementKind.Block) {
					sb.append("\r\n");
					print(whileStatement.Statement, indent + 1, sb);
				} else {
					sb.append(" ");
					print(whileStatement.Statement, indent, sb);
				}
				break;

			case Foreach:
				var foreachStatement = (ForeachStatementNode)statement;
				sb.append(indentText);
				sb.append("foreach (");
				if (foreachStatement.Type != null) {
					print(foreachStatement.Type, sb);
					sb.append(" ");
				} else {
					sb.append("var ");
				}
				sb.append(new String(text, foreachStatement.NameOffset, foreachStatement.NameLength));
				sb.append(" in ");
				print(foreachStatement.Source, sb);
				sb.append(")");
				if (foreachStatement.Statement.StatementKind != StatementKind.Block) {
					sb.append("\r\n");
					print(foreachStatement.Statement, indent + 1, sb);
				} else {
					sb.append(" ");
					print(foreachStatement.Statement, indent, sb);
				}
				break;

			case For:
				var forStatement = (ForStatementNode)statement;
				sb.append(indentText);
				sb.append("for (");
				foreach (var init in forStatement.Initializer) {
					print(init, 0, false, sb);
				}
				sb.append("; ");
				if (forStatement.Condition != null) {
					print(forStatement.Condition, sb);
				}
				sb.append("; ");
				foreach (var iter in forStatement.Iterator) {
					print(iter, 0, false, sb);
				}
				sb.append(")");
				if (forStatement.Statement.StatementKind != StatementKind.Block) {
					sb.append("\r\n");
					print(forStatement.Statement, indent + 1, sb);
				} else {
					sb.append(" ");
					print(forStatement.Statement, indent, sb);
				}
				break;

			default:
				throw new RuntimeException("Statement not supported: " + statement.StatementKind);
			}
		}
		private String statementToString(StatementNode statement) {
			var sb = new StringBuilder();
			switch (statement.StatementKind) {
			case Empty:
				sb.append("#empty;\r\n");
				break;

			case Expression:
				var expression = (ExpressionStatementNode)statement;
				sb.append(expressionToString(expression.Expression));
				sb.append(";\r\n");
				break;
				
			case Block:
				var block = (BlockStatementNode)statement;
				sb.append("{\r\n");
				foreach (var s in block.Statements) {
					sb.append(statementToString(s));
				}
				sb.append("}\r\n");
				break;

			case LocalDeclaration:
				var locals = (LocalDeclarationStatementNode)statement;
				if (locals.Type == null) {
					sb.append("var ");
				} else {
					sb.append(typeReferenceToString(locals.Type));
					sb.append(" ");
				}
				var first = true;
				foreach (var d in locals.Declarators) {
					if (first) {
						first = false;
					} else {
						sb.append(", ");
					}
					sb.append(new String(text, d.NameOffset, d.NameLength));
					if (d.Value != null) {
						sb.append(" = ");
						sb.append(expressionToString(d.Value));
					}
				}
				sb.append(";\r\n");
				break;

			case Return:
				var rt = (ReturnStatementNode)statement;
				sb.append("return");
				if (rt.Value != null) {
					sb.append("(");
					sb.append(expressionToString(rt.Value));
					sb.append(")");

				}
				sb.append(";\r\n");
				break;

			case If:
				var ifStatement = (IfStatementNode)statement;
				sb.append("if (");
				sb.append(expressionToString(ifStatement.Condition));
				sb.append(")\r\n");
				sb.append(statementToString(ifStatement.IfTrue));
				if (ifStatement.IfFalse != null) {
					sb.append("else\r\n");
					sb.append(statementToString(ifStatement.IfFalse));
				}
				break;

			case While:
				var whileStatement = (WhileStatementNode)statement;
				sb.append("while (");
				sb.append(expressionToString(whileStatement.Condition));
				sb.append(")\r\n");
				sb.append(statementToString(whileStatement.Statement));
				break;

			case Do:
				var doStatement = (DoStatementNode)statement;
				sb.append("do ");
				sb.append(statementToString(doStatement.Statement));
				sb.append("while (");
				sb.append(expressionToString(doStatement.Condition));
				sb.append(");\r\n");
				break;

			case For:
				var forStatement = (ForStatementNode)statement;
				sb.append("for (");
				foreach (var init in forStatement.Initializer) {
					sb.append(statementToString(init));
				}
				sb.append("Condition(");
				if (forStatement.Condition != null) {
					sb.append(expressionToString(forStatement.Condition));
				}
				sb.append(");\r\n");
				foreach (var iter in forStatement.Iterator) {
					sb.append(statementToString(iter));
				}
				sb.append(")\r\n");
				sb.append(statementToString(forStatement.Statement));
				break;

			case Try:
				var tryStatement = (TryStatementNode)statement;
				sb.append("try ");
				sb.append(statementToString(tryStatement.Block));
				foreach (var cc in tryStatement.CatchClauses) {
					sb.append("catch ");
					if (cc.ExceptionType != null) {
						sb.append("(");
						sb.append(typeReferenceToString(cc.ExceptionType));
						if (cc.NameLength > 0) {
							sb.append(" ");
							sb.append(new String(text, cc.NameOffset, cc.NameLength));
						}
						sb.append(") ");
					}
					sb.append(statementToString(cc.Block));
				}
				if (tryStatement.Finally != null) {
					sb.append("finally ");
					sb.append(statementToString(tryStatement.Finally));
				}
				break;

			case Switch:
				var switchStatement = (SwitchStatementNode)statement;
				sb.append("switch (");
				sb.append(expressionToString(switchStatement.Selector));
				sb.append(") {\r\n");
				foreach (var ss in switchStatement.Sections) {
					if (ss.CaseExpression != null) {
						sb.append("case ");
						sb.append(expressionToString(ss.CaseExpression));
						sb.append(":\r\n");
					} else {
						sb.append("default:\r\n");
					}
					foreach (var s in ss.Statements) {
						sb.append(statementToString(s));
					}
				}
				sb.append("}\r\n");
				break;

			case Foreach:
				var foreachStatement = (ForeachStatementNode)statement;
				sb.append("foreach (");
				if (foreachStatement.Type != null) {
					sb.append(typeReferenceToString(foreachStatement.Type));
					sb.append(" ");
				} else {
					sb.append("var ");
				}
				sb.append(new String(text, foreachStatement.NameOffset, foreachStatement.NameLength));
				sb.append(" in ");
				sb.append(expressionToString(foreachStatement.Source));
				sb.append(") ");
				sb.append(statementToString(foreachStatement.Statement));
				break;

			case Break:
				sb.append("break;\r\n");
				break;

			case Continue:
				sb.append("continue;\r\n");
				break;

			case GotoCase:
				var gotoCaseStatememt = (GotoCaseStatementNode)statement;
				sb.append("goto ");
				if (gotoCaseStatememt.Expression != null) {
					sb.append("case ");
					sb.append(expressionToString(gotoCaseStatememt.Expression));
				} else {
					sb.append("default");
				}
				sb.append(";\r\n");
				break;

			case Using:
				var usingStatement = (UsingStatementNode)statement;
				sb.append("using (");
				sb.append(statementToString(usingStatement.ResourceAcquisition));
				sb.append(") ");
				sb.append(statementToString(usingStatement.Statement));
				break;

			case Synchronized:
				var lockStatement = (SynchronizedStatementNode)statement;
				sb.append("synchronized (");
				sb.append(expressionToString(lockStatement.Lock));
				sb.append(") ");
				sb.append(statementToString(lockStatement.Statement));
				break;

			case Goto:
				var gotoStatememt = (GotoStatementNode)statement;
				sb.append("goto ");
				sb.append(new String(text, gotoStatememt.LabelOffset, gotoStatememt.LabelLength));
				sb.append(";\r\n");
				break;

			case Labeled:
				var label = (LabeledStatementNode)statement;
				sb.append(new String(text, label.NameOffset, label.NameLength));
				sb.append(": ");
				sb.append(statementToString(label.Statement));
				sb.append(";\r\n");
				break;

			default:
				throw new RuntimeException("Unhandled statement: " + statement.StatementKind);
			}
			return sb.toString();
		}
		private void print(StatementNode s, int indent, StringBuilder sb) {
			print(s, indent, true, sb);
		}