    private StringBuffer printSymbolTree(int linebreakpos, boolean preserveAllSemiColons)
            throws IOException {
        offset = 0;
        braceNesting = 0;
        scopes.clear();
        String symbol;
        JavaScriptToken token;
        JavaScriptToken lastToken = getToken(0);
        ScriptOrFnScope currentScope;
        JavaScriptIdentifier identifier;
        int length = tokens.size();
        StringBuffer result = new StringBuffer();
        int linestartpos = 0;
        enterScope(globalScope);
        while (offset < length) {
            token = consumeToken();
            symbol = token.getValue();
            currentScope = getCurrentScope();
            switch (token.getType()) {
                case Token.GET:
                case Token.SET:
                    lastToken = token;
                case Token.NAME:
                    if (offset >= 2 && getToken(-2).getType() == Token.DOT ||
                            getToken(0).getType() == Token.OBJECTLIT) {
                        result.append(symbol);
                    } else {
                        identifier = getIdentifier(symbol, currentScope);
                        if (identifier != null) {
                            if (identifier.getMungedValue() != null) {
                                result.append(identifier.getMungedValue());
                            } else {
                                result.append(symbol);
                            }
                            if (currentScope != globalScope && identifier.getRefcount() == 0) {
                                warn("The symbol " + symbol + " is declared but is apparently never used.\nThis code can probably be written in a more compact way.", true);
                            }
                        } else {
                            result.append(symbol);
                        }
                    }
                    break;
                case Token.REGEXP:
                case Token.NUMBER:
                case Token.STRING:
                    result.append(symbol);
                    break;
                case Token.ADD:
                case Token.SUB:
                    result.append((String) literals.get(new Integer(token.getType())));
                    if (offset < length) {
                        token = getToken(0);
                        if (token.getType() == Token.INC ||
                                token.getType() == Token.DEC ||
                                token.getType() == Token.ADD ||
                                token.getType() == Token.DEC) {
                            result.append(' ');
                        } else if (token.getType() == Token.POS && getToken(-1).getType() == Token.ADD ||
                                token.getType() == Token.NEG && getToken(-1).getType() == Token.SUB) {
                            result.append(' ');
                        }
                    }
                    break;
                case Token.FUNCTION:
                    if (lastToken.getType() != Token.GET && lastToken.getType() != Token.SET) {
                        result.append("function");
                    }
                    token = consumeToken();
                    if (token.getType() == Token.NAME) {
                        result.append(' ');
                        symbol = token.getValue();
                        identifier = getIdentifier(symbol, currentScope);
                        assert identifier != null;
                        if (identifier.getMungedValue() != null) {
                            result.append(identifier.getMungedValue());
                        } else {
                            result.append(symbol);
                        }
                        if (currentScope != globalScope && identifier.getRefcount() == 0) {
                            warn("The symbol " + symbol + " is declared but is apparently never used.\nThis code can probably be written in a more compact way.", true);
                        }
                        token = consumeToken();
                    }
                    assert token.getType() == Token.LP;
                    result.append('(');
                    currentScope = (ScriptOrFnScope) indexedScopes.get(new Integer(offset));
                    enterScope(currentScope);
                    while ((token = consumeToken()).getType() != Token.RP) {
                        assert token.getType() == Token.NAME || token.getType() == Token.COMMA;
                        if (token.getType() == Token.NAME) {
                            symbol = token.getValue();
                            identifier = getIdentifier(symbol, currentScope);
                            assert identifier != null;
                            if (identifier.getMungedValue() != null) {
                                result.append(identifier.getMungedValue());
                            } else {
                                result.append(symbol);
                            }
                        } else if (token.getType() == Token.COMMA) {
                            result.append(',');
                        }
                    }
                    result.append(')');
                    token = consumeToken();
                    assert token.getType() == Token.LC;
                    result.append('{');
                    braceNesting++;
                    token = getToken(0);
                    if (token.getType() == Token.STRING &&
                            getToken(1).getType() == Token.SEMI) {
                        consumeToken();
                        consumeToken();
                    }
                    break;
                case Token.RETURN:
                case Token.TYPEOF:
                    result.append(literals.get(new Integer(token.getType())));
                    if (offset < length) {
                        token = getToken(0);
                        if (token.getType() != Token.LP &&
                                token.getType() != Token.LB &&
                                token.getType() != Token.LC &&
                                token.getType() != Token.STRING &&
                                token.getType() != Token.REGEXP &&
                                token.getType() != Token.SEMI) {
                            result.append(' ');
                        }
                    }
                    break;
                case Token.CASE:
                case Token.THROW:
                    result.append(literals.get(new Integer(token.getType())));
                    if (offset < length && getToken(0).getType() != Token.STRING) {
                        result.append(' ');
                    }
                    break;
                case Token.BREAK:
                case Token.CONTINUE:
                    result.append(literals.get(new Integer(token.getType())));
                    if (offset < length && getToken(0).getType() != Token.SEMI) {
                        result.append(' ');
                    }
                    break;
                case Token.LC:
                    result.append('{');
                    braceNesting++;
                    break;
                case Token.RC:
                    result.append('}');
                    braceNesting--;
                    assert braceNesting >= currentScope.getBraceNesting();
                    if (braceNesting == currentScope.getBraceNesting()) {
                        leaveCurrentScope();
                    }
                    break;
                case Token.SEMI:
                    if (preserveAllSemiColons || offset < length && getToken(0).getType() != Token.RC) {
                        result.append(';');
                    }
                    if (linebreakpos >= 0 && result.length() - linestartpos > linebreakpos) {
                        result.append('\n');
                        linestartpos = result.length();
                    }
                    break;
                case Token.CONDCOMMENT:
                case Token.KEEPCOMMENT:
                    if (result.length() > 0 && result.charAt(result.length() - 1) != '\n') {
                        result.append("\n");
                    }
                    result.append("/*");
                    result.append(symbol);
                    result.append("*/\n");
                    break;
                default:
                    String literal = (String) literals.get(new Integer(token.getType()));
                    if (literal != null) {
                        result.append(literal);
                    } else {
                        warn("This symbol cannot be printed: " + symbol, true);
                    }
                    break;
            }
        }
        if (!preserveAllSemiColons &&
                result.length() > 0 &&
                getToken(-1).getType() != Token.CONDCOMMENT &&
                getToken(-1).getType() != Token.KEEPCOMMENT) {
            if (result.charAt(result.length() - 1) == '\n') {
                result.setCharAt(result.length() - 1, ';');
            } else {
                result.append(';');
            }
        }
        return result;
    }

    private StringBuffer printSymbolTree(int linebreakpos, boolean preserveAllSemiColons)
            throws IOException {
        offset = 0;
        braceNesting = 0;
        scopes.clear();
        String symbol;
        JavaScriptToken token;
        JavaScriptToken lastToken = getToken(0);
        ScriptOrFnScope currentScope;
        JavaScriptIdentifier identifier;
        int length = tokens.size();
        StringBuffer result = new StringBuffer();
        int linestartpos = 0;
        enterScope(globalScope);
        while (offset < length) {
            token = consumeToken();
            symbol = token.getValue();
            currentScope = getCurrentScope();
            switch (token.getType()) {
                case Token.GET:
                case Token.SET:
                    lastToken = token;
                case Token.NAME:
                    if (offset >= 2 && getToken(-2).getType() == Token.DOT ||
                            getToken(0).getType() == Token.OBJECTLIT) {
                        result.append(symbol);
                    } else {
                        identifier = getIdentifier(symbol, currentScope);
                        if (identifier != null) {
                            if (identifier.getMungedValue() != null) {
                                result.append(identifier.getMungedValue());
                            } else {
                                result.append(symbol);
                            }
                            if (currentScope != globalScope && identifier.getRefcount() == 0) {
                                warn("The symbol " + symbol + " is declared but is apparently never used.\nThis code can probably be written in a more compact way.", true);
                            }
                        } else {
                            result.append(symbol);
                        }
                    }
                    break;
                case Token.REGEXP:
                case Token.NUMBER:
                case Token.STRING:
                    result.append(symbol);
                    break;
                case Token.ADD:
                case Token.SUB:
                    result.append((String) literals.get(new Integer(token.getType())));
                    if (offset < length) {
                        token = getToken(0);
                        if (token.getType() == Token.INC ||
                                token.getType() == Token.DEC ||
                                token.getType() == Token.ADD ||
                                token.getType() == Token.DEC) {
                            result.append(' ');
                        } else if (token.getType() == Token.POS && getToken(-1).getType() == Token.ADD ||
                                token.getType() == Token.NEG && getToken(-1).getType() == Token.SUB) {
                            result.append(' ');
                        }
                    }
                    break;
                case Token.FUNCTION:
                    if (lastToken.getType() != Token.GET && lastToken.getType() != Token.SET) {
                        result.append("function");
                    }
                    lastToken = token;
                    token = consumeToken();
                    if (token.getType() == Token.NAME) {
                        result.append(' ');
                        symbol = token.getValue();
                        identifier = getIdentifier(symbol, currentScope);
                        assert identifier != null;
                        if (identifier.getMungedValue() != null) {
                            result.append(identifier.getMungedValue());
                        } else {
                            result.append(symbol);
                        }
                        if (currentScope != globalScope && identifier.getRefcount() == 0) {
                            warn("The symbol " + symbol + " is declared but is apparently never used.\nThis code can probably be written in a more compact way.", true);
                        }
                        token = consumeToken();
                    }
                    assert token.getType() == Token.LP;
                    result.append('(');
                    currentScope = (ScriptOrFnScope) indexedScopes.get(new Integer(offset));
                    enterScope(currentScope);
                    while ((token = consumeToken()).getType() != Token.RP) {
                        assert token.getType() == Token.NAME || token.getType() == Token.COMMA;
                        if (token.getType() == Token.NAME) {
                            symbol = token.getValue();
                            identifier = getIdentifier(symbol, currentScope);
                            assert identifier != null;
                            if (identifier.getMungedValue() != null) {
                                result.append(identifier.getMungedValue());
                            } else {
                                result.append(symbol);
                            }
                        } else if (token.getType() == Token.COMMA) {
                            result.append(',');
                        }
                    }
                    result.append(')');
                    token = consumeToken();
                    assert token.getType() == Token.LC;
                    result.append('{');
                    braceNesting++;
                    token = getToken(0);
                    if (token.getType() == Token.STRING &&
                            getToken(1).getType() == Token.SEMI) {
                        consumeToken();
                        consumeToken();
                    }
                    break;
                case Token.RETURN:
                case Token.TYPEOF:
                    result.append(literals.get(new Integer(token.getType())));
                    if (offset < length) {
                        token = getToken(0);
                        if (token.getType() != Token.LP &&
                                token.getType() != Token.LB &&
                                token.getType() != Token.LC &&
                                token.getType() != Token.STRING &&
                                token.getType() != Token.REGEXP &&
                                token.getType() != Token.SEMI) {
                            result.append(' ');
                        }
                    }
                    break;
                case Token.CASE:
                case Token.THROW:
                    result.append(literals.get(new Integer(token.getType())));
                    if (offset < length && getToken(0).getType() != Token.STRING) {
                        result.append(' ');
                    }
                    break;
                case Token.BREAK:
                case Token.CONTINUE:
                    result.append(literals.get(new Integer(token.getType())));
                    if (offset < length && getToken(0).getType() != Token.SEMI) {
                        result.append(' ');
                    }
                    break;
                case Token.LC:
                    result.append('{');
                    braceNesting++;
                    break;
                case Token.RC:
                    result.append('}');
                    braceNesting--;
                    assert braceNesting >= currentScope.getBraceNesting();
                    if (braceNesting == currentScope.getBraceNesting()) {
                        leaveCurrentScope();
                    }
                    break;
                case Token.SEMI:
                    if (preserveAllSemiColons || offset < length && getToken(0).getType() != Token.RC) {
                        result.append(';');
                    }
                    if (linebreakpos >= 0 && result.length() - linestartpos > linebreakpos) {
                        result.append('\n');
                        linestartpos = result.length();
                    }
                    break;
                case Token.CONDCOMMENT:
                case Token.KEEPCOMMENT:
                    if (result.length() > 0 && result.charAt(result.length() - 1) != '\n') {
                        result.append("\n");
                    }
                    result.append("/*");
                    result.append(symbol);
                    result.append("*/\n");
                    break;
                default:
                    String literal = (String) literals.get(new Integer(token.getType()));
                    if (literal != null) {
                        result.append(literal);
                    } else {
                        warn("This symbol cannot be printed: " + symbol, true);
                    }
                    break;
            }
        }
        if (!preserveAllSemiColons &&
                result.length() > 0 &&
                getToken(-1).getType() != Token.CONDCOMMENT &&
                getToken(-1).getType() != Token.KEEPCOMMENT) {
            if (result.charAt(result.length() - 1) == '\n') {
                result.setCharAt(result.length() - 1, ';');
            } else {
                result.append(';');
            }
        }
        return result;
    }
