1beca7ae1f35e7f6605f4a21b5f4edeaa72000e39Chris Craik# This code is original from jsmin by Douglas Crockford, it was translated to
2beca7ae1f35e7f6605f4a21b5f4edeaa72000e39Chris Craik# Python by Baruch Even. It was rewritten by Dave St.Germain for speed.
3beca7ae1f35e7f6605f4a21b5f4edeaa72000e39Chris Craik#
4beca7ae1f35e7f6605f4a21b5f4edeaa72000e39Chris Craik# The MIT License (MIT)
5beca7ae1f35e7f6605f4a21b5f4edeaa72000e39Chris Craik#
6beca7ae1f35e7f6605f4a21b5f4edeaa72000e39Chris Craik# Copyright (c) 2013 Dave St.Germain
7beca7ae1f35e7f6605f4a21b5f4edeaa72000e39Chris Craik#
8beca7ae1f35e7f6605f4a21b5f4edeaa72000e39Chris Craik# Permission is hereby granted, free of charge, to any person obtaining a copy
9beca7ae1f35e7f6605f4a21b5f4edeaa72000e39Chris Craik# of this software and associated documentation files (the "Software"), to deal
10beca7ae1f35e7f6605f4a21b5f4edeaa72000e39Chris Craik# in the Software without restriction, including without limitation the rights
11beca7ae1f35e7f6605f4a21b5f4edeaa72000e39Chris Craik# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12beca7ae1f35e7f6605f4a21b5f4edeaa72000e39Chris Craik# copies of the Software, and to permit persons to whom the Software is
13beca7ae1f35e7f6605f4a21b5f4edeaa72000e39Chris Craik# furnished to do so, subject to the following conditions:
14beca7ae1f35e7f6605f4a21b5f4edeaa72000e39Chris Craik#
15beca7ae1f35e7f6605f4a21b5f4edeaa72000e39Chris Craik# The above copyright notice and this permission notice shall be included in
16beca7ae1f35e7f6605f4a21b5f4edeaa72000e39Chris Craik# all copies or substantial portions of the Software.
17beca7ae1f35e7f6605f4a21b5f4edeaa72000e39Chris Craik#
18beca7ae1f35e7f6605f4a21b5f4edeaa72000e39Chris Craik# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19beca7ae1f35e7f6605f4a21b5f4edeaa72000e39Chris Craik# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20beca7ae1f35e7f6605f4a21b5f4edeaa72000e39Chris Craik# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21beca7ae1f35e7f6605f4a21b5f4edeaa72000e39Chris Craik# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22beca7ae1f35e7f6605f4a21b5f4edeaa72000e39Chris Craik# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23beca7ae1f35e7f6605f4a21b5f4edeaa72000e39Chris Craik# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
24beca7ae1f35e7f6605f4a21b5f4edeaa72000e39Chris Craik# THE SOFTWARE.
25beca7ae1f35e7f6605f4a21b5f4edeaa72000e39Chris Craik
26beca7ae1f35e7f6605f4a21b5f4edeaa72000e39Chris Craik
27beca7ae1f35e7f6605f4a21b5f4edeaa72000e39Chris Craikimport sys
28beca7ae1f35e7f6605f4a21b5f4edeaa72000e39Chris Craikis_3 = sys.version_info >= (3, 0)
29beca7ae1f35e7f6605f4a21b5f4edeaa72000e39Chris Craikif is_3:
30beca7ae1f35e7f6605f4a21b5f4edeaa72000e39Chris Craik    import io
31beca7ae1f35e7f6605f4a21b5f4edeaa72000e39Chris Craikelse:
32beca7ae1f35e7f6605f4a21b5f4edeaa72000e39Chris Craik    import StringIO
33beca7ae1f35e7f6605f4a21b5f4edeaa72000e39Chris Craik    try:
34beca7ae1f35e7f6605f4a21b5f4edeaa72000e39Chris Craik        import cStringIO
35beca7ae1f35e7f6605f4a21b5f4edeaa72000e39Chris Craik    except ImportError:
36beca7ae1f35e7f6605f4a21b5f4edeaa72000e39Chris Craik        cStringIO = None
37beca7ae1f35e7f6605f4a21b5f4edeaa72000e39Chris Craik
38beca7ae1f35e7f6605f4a21b5f4edeaa72000e39Chris Craik
39beca7ae1f35e7f6605f4a21b5f4edeaa72000e39Chris Craik__all__ = ['jsmin', 'JavascriptMinify']
40beca7ae1f35e7f6605f4a21b5f4edeaa72000e39Chris Craik__version__ = '2.0.9'
41beca7ae1f35e7f6605f4a21b5f4edeaa72000e39Chris Craik
42beca7ae1f35e7f6605f4a21b5f4edeaa72000e39Chris Craik
43beca7ae1f35e7f6605f4a21b5f4edeaa72000e39Chris Craikdef jsmin(js):
44beca7ae1f35e7f6605f4a21b5f4edeaa72000e39Chris Craik    """
45beca7ae1f35e7f6605f4a21b5f4edeaa72000e39Chris Craik    returns a minified version of the javascript string
46beca7ae1f35e7f6605f4a21b5f4edeaa72000e39Chris Craik    """
47beca7ae1f35e7f6605f4a21b5f4edeaa72000e39Chris Craik    if not is_3:
48beca7ae1f35e7f6605f4a21b5f4edeaa72000e39Chris Craik        if cStringIO and not isinstance(js, unicode):
49beca7ae1f35e7f6605f4a21b5f4edeaa72000e39Chris Craik            # strings can use cStringIO for a 3x performance
50beca7ae1f35e7f6605f4a21b5f4edeaa72000e39Chris Craik            # improvement, but unicode (in python2) cannot
51beca7ae1f35e7f6605f4a21b5f4edeaa72000e39Chris Craik            klass = cStringIO.StringIO
52beca7ae1f35e7f6605f4a21b5f4edeaa72000e39Chris Craik        else:
53beca7ae1f35e7f6605f4a21b5f4edeaa72000e39Chris Craik            klass = StringIO.StringIO
54beca7ae1f35e7f6605f4a21b5f4edeaa72000e39Chris Craik    else:
55beca7ae1f35e7f6605f4a21b5f4edeaa72000e39Chris Craik        klass = io.StringIO
56beca7ae1f35e7f6605f4a21b5f4edeaa72000e39Chris Craik    ins = klass(js)
57beca7ae1f35e7f6605f4a21b5f4edeaa72000e39Chris Craik    outs = klass()
58beca7ae1f35e7f6605f4a21b5f4edeaa72000e39Chris Craik    JavascriptMinify(ins, outs).minify()
59beca7ae1f35e7f6605f4a21b5f4edeaa72000e39Chris Craik    return outs.getvalue()
60beca7ae1f35e7f6605f4a21b5f4edeaa72000e39Chris Craik
61beca7ae1f35e7f6605f4a21b5f4edeaa72000e39Chris Craik
62beca7ae1f35e7f6605f4a21b5f4edeaa72000e39Chris Craikclass JavascriptMinify(object):
63beca7ae1f35e7f6605f4a21b5f4edeaa72000e39Chris Craik    """
64beca7ae1f35e7f6605f4a21b5f4edeaa72000e39Chris Craik    Minify an input stream of javascript, writing
65beca7ae1f35e7f6605f4a21b5f4edeaa72000e39Chris Craik    to an output stream
66beca7ae1f35e7f6605f4a21b5f4edeaa72000e39Chris Craik    """
67beca7ae1f35e7f6605f4a21b5f4edeaa72000e39Chris Craik
68beca7ae1f35e7f6605f4a21b5f4edeaa72000e39Chris Craik    def __init__(self, instream=None, outstream=None):
69beca7ae1f35e7f6605f4a21b5f4edeaa72000e39Chris Craik        self.ins = instream
70beca7ae1f35e7f6605f4a21b5f4edeaa72000e39Chris Craik        self.outs = outstream
71beca7ae1f35e7f6605f4a21b5f4edeaa72000e39Chris Craik
72beca7ae1f35e7f6605f4a21b5f4edeaa72000e39Chris Craik    def minify(self, instream=None, outstream=None):
73beca7ae1f35e7f6605f4a21b5f4edeaa72000e39Chris Craik        if instream and outstream:
74beca7ae1f35e7f6605f4a21b5f4edeaa72000e39Chris Craik            self.ins, self.outs = instream, outstream
75beca7ae1f35e7f6605f4a21b5f4edeaa72000e39Chris Craik
76beca7ae1f35e7f6605f4a21b5f4edeaa72000e39Chris Craik        self.is_return = False
77beca7ae1f35e7f6605f4a21b5f4edeaa72000e39Chris Craik        self.return_buf = ''
78beca7ae1f35e7f6605f4a21b5f4edeaa72000e39Chris Craik
79beca7ae1f35e7f6605f4a21b5f4edeaa72000e39Chris Craik        def write(char):
80beca7ae1f35e7f6605f4a21b5f4edeaa72000e39Chris Craik            # all of this is to support literal regular expressions.
81beca7ae1f35e7f6605f4a21b5f4edeaa72000e39Chris Craik            # sigh
82beca7ae1f35e7f6605f4a21b5f4edeaa72000e39Chris Craik            if char in 'return':
83beca7ae1f35e7f6605f4a21b5f4edeaa72000e39Chris Craik                self.return_buf += char
84beca7ae1f35e7f6605f4a21b5f4edeaa72000e39Chris Craik                self.is_return = self.return_buf == 'return'
85beca7ae1f35e7f6605f4a21b5f4edeaa72000e39Chris Craik            self.outs.write(char)
86beca7ae1f35e7f6605f4a21b5f4edeaa72000e39Chris Craik            if self.is_return:
87beca7ae1f35e7f6605f4a21b5f4edeaa72000e39Chris Craik                self.return_buf = ''
88beca7ae1f35e7f6605f4a21b5f4edeaa72000e39Chris Craik
89beca7ae1f35e7f6605f4a21b5f4edeaa72000e39Chris Craik        read = self.ins.read
90beca7ae1f35e7f6605f4a21b5f4edeaa72000e39Chris Craik
91beca7ae1f35e7f6605f4a21b5f4edeaa72000e39Chris Craik        space_strings = "abcdefghijklmnopqrstuvwxyz"\
92beca7ae1f35e7f6605f4a21b5f4edeaa72000e39Chris Craik        "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_$\\"
93beca7ae1f35e7f6605f4a21b5f4edeaa72000e39Chris Craik        starters, enders = '{[(+-', '}])+-"\''
94beca7ae1f35e7f6605f4a21b5f4edeaa72000e39Chris Craik        newlinestart_strings = starters + space_strings
95beca7ae1f35e7f6605f4a21b5f4edeaa72000e39Chris Craik        newlineend_strings = enders + space_strings
96beca7ae1f35e7f6605f4a21b5f4edeaa72000e39Chris Craik        do_newline = False
97beca7ae1f35e7f6605f4a21b5f4edeaa72000e39Chris Craik        do_space = False
98beca7ae1f35e7f6605f4a21b5f4edeaa72000e39Chris Craik        escape_slash_count = 0
99beca7ae1f35e7f6605f4a21b5f4edeaa72000e39Chris Craik        doing_single_comment = False
100beca7ae1f35e7f6605f4a21b5f4edeaa72000e39Chris Craik        previous_before_comment = ''
101beca7ae1f35e7f6605f4a21b5f4edeaa72000e39Chris Craik        doing_multi_comment = False
102beca7ae1f35e7f6605f4a21b5f4edeaa72000e39Chris Craik        in_re = False
103beca7ae1f35e7f6605f4a21b5f4edeaa72000e39Chris Craik        in_quote = ''
104beca7ae1f35e7f6605f4a21b5f4edeaa72000e39Chris Craik        quote_buf = []
105beca7ae1f35e7f6605f4a21b5f4edeaa72000e39Chris Craik
106beca7ae1f35e7f6605f4a21b5f4edeaa72000e39Chris Craik        previous = read(1)
107beca7ae1f35e7f6605f4a21b5f4edeaa72000e39Chris Craik        if previous == '\\':
108beca7ae1f35e7f6605f4a21b5f4edeaa72000e39Chris Craik            escape_slash_count += 1
109beca7ae1f35e7f6605f4a21b5f4edeaa72000e39Chris Craik        next1 = read(1)
110beca7ae1f35e7f6605f4a21b5f4edeaa72000e39Chris Craik        if previous == '/':
111beca7ae1f35e7f6605f4a21b5f4edeaa72000e39Chris Craik            if next1 == '/':
112beca7ae1f35e7f6605f4a21b5f4edeaa72000e39Chris Craik                doing_single_comment = True
113beca7ae1f35e7f6605f4a21b5f4edeaa72000e39Chris Craik            elif next1 == '*':
114beca7ae1f35e7f6605f4a21b5f4edeaa72000e39Chris Craik                doing_multi_comment = True
115beca7ae1f35e7f6605f4a21b5f4edeaa72000e39Chris Craik                previous = next1
116beca7ae1f35e7f6605f4a21b5f4edeaa72000e39Chris Craik                next1 = read(1)
117beca7ae1f35e7f6605f4a21b5f4edeaa72000e39Chris Craik            else:
118beca7ae1f35e7f6605f4a21b5f4edeaa72000e39Chris Craik                write(previous)
119beca7ae1f35e7f6605f4a21b5f4edeaa72000e39Chris Craik        elif not previous:
120beca7ae1f35e7f6605f4a21b5f4edeaa72000e39Chris Craik            return
121beca7ae1f35e7f6605f4a21b5f4edeaa72000e39Chris Craik        elif previous >= '!':
122beca7ae1f35e7f6605f4a21b5f4edeaa72000e39Chris Craik            if previous in "'\"":
123beca7ae1f35e7f6605f4a21b5f4edeaa72000e39Chris Craik                in_quote = previous
124beca7ae1f35e7f6605f4a21b5f4edeaa72000e39Chris Craik            write(previous)
125beca7ae1f35e7f6605f4a21b5f4edeaa72000e39Chris Craik            previous_non_space = previous
126beca7ae1f35e7f6605f4a21b5f4edeaa72000e39Chris Craik        else:
127beca7ae1f35e7f6605f4a21b5f4edeaa72000e39Chris Craik            previous_non_space = ' '
128beca7ae1f35e7f6605f4a21b5f4edeaa72000e39Chris Craik        if not next1:
129beca7ae1f35e7f6605f4a21b5f4edeaa72000e39Chris Craik            return
130beca7ae1f35e7f6605f4a21b5f4edeaa72000e39Chris Craik
131beca7ae1f35e7f6605f4a21b5f4edeaa72000e39Chris Craik        while 1:
132beca7ae1f35e7f6605f4a21b5f4edeaa72000e39Chris Craik            next2 = read(1)
133beca7ae1f35e7f6605f4a21b5f4edeaa72000e39Chris Craik            if not next2:
134beca7ae1f35e7f6605f4a21b5f4edeaa72000e39Chris Craik                last = next1.strip()
135beca7ae1f35e7f6605f4a21b5f4edeaa72000e39Chris Craik                if not (doing_single_comment or doing_multi_comment)\
136beca7ae1f35e7f6605f4a21b5f4edeaa72000e39Chris Craik                    and last not in ('', '/'):
137beca7ae1f35e7f6605f4a21b5f4edeaa72000e39Chris Craik                    if in_quote:
138beca7ae1f35e7f6605f4a21b5f4edeaa72000e39Chris Craik                        write(''.join(quote_buf))
139beca7ae1f35e7f6605f4a21b5f4edeaa72000e39Chris Craik                    write(last)
140beca7ae1f35e7f6605f4a21b5f4edeaa72000e39Chris Craik                break
141beca7ae1f35e7f6605f4a21b5f4edeaa72000e39Chris Craik            if doing_multi_comment:
142beca7ae1f35e7f6605f4a21b5f4edeaa72000e39Chris Craik                if next1 == '*' and next2 == '/':
143beca7ae1f35e7f6605f4a21b5f4edeaa72000e39Chris Craik                    doing_multi_comment = False
144beca7ae1f35e7f6605f4a21b5f4edeaa72000e39Chris Craik                    next2 = read(1)
145beca7ae1f35e7f6605f4a21b5f4edeaa72000e39Chris Craik            elif doing_single_comment:
146beca7ae1f35e7f6605f4a21b5f4edeaa72000e39Chris Craik                if next1 in '\r\n':
147beca7ae1f35e7f6605f4a21b5f4edeaa72000e39Chris Craik                    doing_single_comment = False
148beca7ae1f35e7f6605f4a21b5f4edeaa72000e39Chris Craik                    while next2 in '\r\n':
149beca7ae1f35e7f6605f4a21b5f4edeaa72000e39Chris Craik                        next2 = read(1)
150beca7ae1f35e7f6605f4a21b5f4edeaa72000e39Chris Craik                        if not next2:
151beca7ae1f35e7f6605f4a21b5f4edeaa72000e39Chris Craik                            break
152beca7ae1f35e7f6605f4a21b5f4edeaa72000e39Chris Craik                    if previous_before_comment in ')}]':
153beca7ae1f35e7f6605f4a21b5f4edeaa72000e39Chris Craik                        do_newline = True
154beca7ae1f35e7f6605f4a21b5f4edeaa72000e39Chris Craik                    elif previous_before_comment in space_strings:
155beca7ae1f35e7f6605f4a21b5f4edeaa72000e39Chris Craik                        write('\n')
156beca7ae1f35e7f6605f4a21b5f4edeaa72000e39Chris Craik            elif in_quote:
157beca7ae1f35e7f6605f4a21b5f4edeaa72000e39Chris Craik                quote_buf.append(next1)
158beca7ae1f35e7f6605f4a21b5f4edeaa72000e39Chris Craik
159beca7ae1f35e7f6605f4a21b5f4edeaa72000e39Chris Craik                if next1 == in_quote:
160beca7ae1f35e7f6605f4a21b5f4edeaa72000e39Chris Craik                    numslashes = 0
161beca7ae1f35e7f6605f4a21b5f4edeaa72000e39Chris Craik                    for c in reversed(quote_buf[:-1]):
162beca7ae1f35e7f6605f4a21b5f4edeaa72000e39Chris Craik                        if c != '\\':
163beca7ae1f35e7f6605f4a21b5f4edeaa72000e39Chris Craik                            break
164beca7ae1f35e7f6605f4a21b5f4edeaa72000e39Chris Craik                        else:
165beca7ae1f35e7f6605f4a21b5f4edeaa72000e39Chris Craik                            numslashes += 1
166beca7ae1f35e7f6605f4a21b5f4edeaa72000e39Chris Craik                    if numslashes % 2 == 0:
167beca7ae1f35e7f6605f4a21b5f4edeaa72000e39Chris Craik                        in_quote = ''
168beca7ae1f35e7f6605f4a21b5f4edeaa72000e39Chris Craik                        write(''.join(quote_buf))
169beca7ae1f35e7f6605f4a21b5f4edeaa72000e39Chris Craik            elif next1 in '\r\n':
170beca7ae1f35e7f6605f4a21b5f4edeaa72000e39Chris Craik                if previous_non_space in newlineend_strings \
171beca7ae1f35e7f6605f4a21b5f4edeaa72000e39Chris Craik                    or previous_non_space > '~':
172beca7ae1f35e7f6605f4a21b5f4edeaa72000e39Chris Craik                    while 1:
173beca7ae1f35e7f6605f4a21b5f4edeaa72000e39Chris Craik                        if next2 < '!':
174beca7ae1f35e7f6605f4a21b5f4edeaa72000e39Chris Craik                            next2 = read(1)
175beca7ae1f35e7f6605f4a21b5f4edeaa72000e39Chris Craik                            if not next2:
176beca7ae1f35e7f6605f4a21b5f4edeaa72000e39Chris Craik                                break
177beca7ae1f35e7f6605f4a21b5f4edeaa72000e39Chris Craik                        else:
178beca7ae1f35e7f6605f4a21b5f4edeaa72000e39Chris Craik                            if next2 in newlinestart_strings \
179beca7ae1f35e7f6605f4a21b5f4edeaa72000e39Chris Craik                                or next2 > '~' or next2 == '/':
180beca7ae1f35e7f6605f4a21b5f4edeaa72000e39Chris Craik                                do_newline = True
181beca7ae1f35e7f6605f4a21b5f4edeaa72000e39Chris Craik                            break
182beca7ae1f35e7f6605f4a21b5f4edeaa72000e39Chris Craik            elif next1 < '!' and not in_re:
183beca7ae1f35e7f6605f4a21b5f4edeaa72000e39Chris Craik                if (previous_non_space in space_strings \
184beca7ae1f35e7f6605f4a21b5f4edeaa72000e39Chris Craik                    or previous_non_space > '~') \
185beca7ae1f35e7f6605f4a21b5f4edeaa72000e39Chris Craik                    and (next2 in space_strings or next2 > '~'):
186beca7ae1f35e7f6605f4a21b5f4edeaa72000e39Chris Craik                    do_space = True
187beca7ae1f35e7f6605f4a21b5f4edeaa72000e39Chris Craik                elif previous_non_space in '-+' and next2 == previous_non_space:
188beca7ae1f35e7f6605f4a21b5f4edeaa72000e39Chris Craik                    # protect against + ++ or - -- sequences
189beca7ae1f35e7f6605f4a21b5f4edeaa72000e39Chris Craik                    do_space = True
190beca7ae1f35e7f6605f4a21b5f4edeaa72000e39Chris Craik                elif self.is_return and next2 == '/':
191beca7ae1f35e7f6605f4a21b5f4edeaa72000e39Chris Craik                    # returning a regex...
192beca7ae1f35e7f6605f4a21b5f4edeaa72000e39Chris Craik                    write(' ')
193beca7ae1f35e7f6605f4a21b5f4edeaa72000e39Chris Craik            elif next1 == '/':
194beca7ae1f35e7f6605f4a21b5f4edeaa72000e39Chris Craik                if do_space:
195beca7ae1f35e7f6605f4a21b5f4edeaa72000e39Chris Craik                    write(' ')
196beca7ae1f35e7f6605f4a21b5f4edeaa72000e39Chris Craik                if in_re:
197beca7ae1f35e7f6605f4a21b5f4edeaa72000e39Chris Craik                    if previous != '\\' or (not escape_slash_count % 2) or next2 in 'gimy':
198beca7ae1f35e7f6605f4a21b5f4edeaa72000e39Chris Craik                        in_re = False
199beca7ae1f35e7f6605f4a21b5f4edeaa72000e39Chris Craik                    write('/')
200beca7ae1f35e7f6605f4a21b5f4edeaa72000e39Chris Craik                elif next2 == '/':
201beca7ae1f35e7f6605f4a21b5f4edeaa72000e39Chris Craik                    doing_single_comment = True
202beca7ae1f35e7f6605f4a21b5f4edeaa72000e39Chris Craik                    previous_before_comment = previous_non_space
203beca7ae1f35e7f6605f4a21b5f4edeaa72000e39Chris Craik                elif next2 == '*':
204beca7ae1f35e7f6605f4a21b5f4edeaa72000e39Chris Craik                    doing_multi_comment = True
205beca7ae1f35e7f6605f4a21b5f4edeaa72000e39Chris Craik                    previous = next1
206beca7ae1f35e7f6605f4a21b5f4edeaa72000e39Chris Craik                    next1 = next2
207beca7ae1f35e7f6605f4a21b5f4edeaa72000e39Chris Craik                    next2 = read(1)
208beca7ae1f35e7f6605f4a21b5f4edeaa72000e39Chris Craik                else:
209beca7ae1f35e7f6605f4a21b5f4edeaa72000e39Chris Craik                    in_re = previous_non_space in '(,=:[?!&|' or self.is_return # literal regular expression
210beca7ae1f35e7f6605f4a21b5f4edeaa72000e39Chris Craik                    write('/')
211beca7ae1f35e7f6605f4a21b5f4edeaa72000e39Chris Craik            else:
212beca7ae1f35e7f6605f4a21b5f4edeaa72000e39Chris Craik                if do_space:
213beca7ae1f35e7f6605f4a21b5f4edeaa72000e39Chris Craik                    do_space = False
214beca7ae1f35e7f6605f4a21b5f4edeaa72000e39Chris Craik                    write(' ')
215beca7ae1f35e7f6605f4a21b5f4edeaa72000e39Chris Craik                if do_newline:
216beca7ae1f35e7f6605f4a21b5f4edeaa72000e39Chris Craik                    write('\n')
217beca7ae1f35e7f6605f4a21b5f4edeaa72000e39Chris Craik                    do_newline = False
218beca7ae1f35e7f6605f4a21b5f4edeaa72000e39Chris Craik
219beca7ae1f35e7f6605f4a21b5f4edeaa72000e39Chris Craik                write(next1)
220beca7ae1f35e7f6605f4a21b5f4edeaa72000e39Chris Craik                if not in_re and next1 in "'\"":
221beca7ae1f35e7f6605f4a21b5f4edeaa72000e39Chris Craik                    in_quote = next1
222beca7ae1f35e7f6605f4a21b5f4edeaa72000e39Chris Craik                    quote_buf = []
223beca7ae1f35e7f6605f4a21b5f4edeaa72000e39Chris Craik
224beca7ae1f35e7f6605f4a21b5f4edeaa72000e39Chris Craik            previous = next1
225beca7ae1f35e7f6605f4a21b5f4edeaa72000e39Chris Craik            next1 = next2
226beca7ae1f35e7f6605f4a21b5f4edeaa72000e39Chris Craik
227beca7ae1f35e7f6605f4a21b5f4edeaa72000e39Chris Craik            if previous >= '!':
228beca7ae1f35e7f6605f4a21b5f4edeaa72000e39Chris Craik                previous_non_space = previous
229beca7ae1f35e7f6605f4a21b5f4edeaa72000e39Chris Craik
230beca7ae1f35e7f6605f4a21b5f4edeaa72000e39Chris Craik            if previous == '\\':
231beca7ae1f35e7f6605f4a21b5f4edeaa72000e39Chris Craik                escape_slash_count += 1
232beca7ae1f35e7f6605f4a21b5f4edeaa72000e39Chris Craik            else:
233beca7ae1f35e7f6605f4a21b5f4edeaa72000e39Chris Craik                escape_slash_count = 0
234