647b024120
also move playground out of the benchmark suite
240 lines
7.3 KiB
Python
240 lines
7.3 KiB
Python
#!/usr/bin/env python
|
|
|
|
# This code is original from jsmin.c by Douglas Crockford. This is a
|
|
# playground port for understanding the semantics by Andr\xe9 Malo. Here's the
|
|
# jsmin.c license:
|
|
#
|
|
# /* jsmin.c
|
|
# 2007-01-08
|
|
#
|
|
# Copyright (c) 2002 Douglas Crockford (www.crockford.com)
|
|
#
|
|
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
# of this software and associated documentation files (the "Software"), to deal
|
|
# in the Software without restriction, including without limitation the rights
|
|
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
# copies of the Software, and to permit persons to whom the Software is
|
|
# furnished to do so, subject to the following conditions:
|
|
#
|
|
# The above copyright notice and this permission notice shall be included in
|
|
# all copies or substantial portions of the Software.
|
|
#
|
|
# The Software shall be used for Good, not Evil.
|
|
#
|
|
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
# SOFTWARE.
|
|
# */
|
|
|
|
import re as _re
|
|
try:
|
|
import cStringIO as _string_io
|
|
except ImportError:
|
|
try:
|
|
import StringIO as _string_io
|
|
except ImportError:
|
|
import io as _string_io
|
|
|
|
|
|
class JSMinError(Exception):
|
|
""" Minifier base error """
|
|
|
|
class UnterminatedComment(JSMinError):
|
|
""" Unterminated comment """
|
|
|
|
class UnterminatedRegex(JSMinError):
|
|
""" Unterminated Regex literal """
|
|
|
|
class UnterminatedString(JSMinError):
|
|
""" Unterminated String literal """
|
|
|
|
|
|
class PrintableLookAheadStream(object):
|
|
""" Stream wrapper providing get and peek methods """
|
|
|
|
def __init__(self, stream):
|
|
""" Initialization """
|
|
self._stream = stream
|
|
self._looked = None
|
|
|
|
def get(self):
|
|
""" Get one character """
|
|
char, self._looked = self._looked, None
|
|
if char is not None:
|
|
return char
|
|
char = self._stream.read(1)
|
|
if not char:
|
|
raise EOFError()
|
|
if char == '\r':
|
|
return '\n'
|
|
if ord(char) >= 32 or char == '\n':
|
|
return char
|
|
return ' '
|
|
|
|
def peek(self):
|
|
""" Peek one character ahead """
|
|
self._looked = self.get()
|
|
return self._looked
|
|
|
|
|
|
def m(regex):
|
|
""" Regex -> matcher """
|
|
return _re.compile(regex).match
|
|
|
|
|
|
def next_char(stream):
|
|
""" Next character leaving out comments """
|
|
get, peek = stream.get, stream.peek
|
|
char = get()
|
|
if char == '/':
|
|
try:
|
|
c_next = peek()
|
|
except EOFError:
|
|
return char
|
|
if c_next == '/': # // comment
|
|
while char != '\n':
|
|
char = get()
|
|
elif c_next == '*': # /* comment */
|
|
get()
|
|
try:
|
|
while not(char == '*' and peek() == '/'):
|
|
char = get()
|
|
get()
|
|
char = ' '
|
|
except EOFError:
|
|
raise UnterminatedComment()
|
|
return char
|
|
|
|
|
|
def action3(stream, out, first, second,
|
|
pre_regex_match=m(r'[(,=:\[!&|?{};\n]')):
|
|
""" throw away second, skip comments and regexps """
|
|
try:
|
|
second = next_char(stream)
|
|
except EOFError:
|
|
out.write(first)
|
|
raise
|
|
if second == '/' and pre_regex_match(first):
|
|
# Regex found. Parse it till the end.
|
|
write, get = out.write, stream.get
|
|
write(first)
|
|
write(second)
|
|
try:
|
|
first = get()
|
|
while first != '/':
|
|
if first == '\\':
|
|
write(first)
|
|
first = get()
|
|
elif first == '[':
|
|
write(first)
|
|
first = get()
|
|
while first != ']':
|
|
if first == '\\':
|
|
write(first)
|
|
first = get()
|
|
write(first)
|
|
first = get()
|
|
write(first)
|
|
first = get()
|
|
second = next_char(stream)
|
|
except EOFError:
|
|
raise UnterminatedRegex()
|
|
return first, second
|
|
|
|
|
|
def action2(stream, out, first, second):
|
|
""" shift, skip strings, comments and regexps """
|
|
first = second
|
|
if first in (r''''"'''):
|
|
quote = first
|
|
write, get = out.write, stream.get
|
|
write(first)
|
|
try:
|
|
first = get()
|
|
while first != quote:
|
|
if first == '\\':
|
|
write(first)
|
|
first = get()
|
|
write(first)
|
|
first = get()
|
|
except EOFError:
|
|
raise UnterminatedString()
|
|
return action3(stream, out, first, second)
|
|
|
|
|
|
def action1(stream, out, first, second):
|
|
""" write, shift, skip strings, comments and regexps """
|
|
out.write(first)
|
|
return action2(stream, out, first, second)
|
|
|
|
|
|
def jsmin_stream(stream, out,
|
|
id_literal=m(r'[a-zA-Z0-9_$\\\177-\377]'),
|
|
open_match=m(r'[{\[(+-]'),
|
|
close_match=m(r'[}\])+"\047-]')):
|
|
"""
|
|
JSMin after jsmin.c by Douglas Crockford
|
|
|
|
http://www.crockford.com/javascript/jsmin.c
|
|
"""
|
|
# pylint: disable = R0912
|
|
stream = PrintableLookAheadStream(stream)
|
|
try:
|
|
first, second = action3(stream, out, '\n', None)
|
|
while 1:
|
|
if first == ' ':
|
|
if id_literal(second):
|
|
first, second = action1(stream, out, first, second)
|
|
else:
|
|
first, second = action2(stream, out, first, second)
|
|
elif first == '\n':
|
|
if open_match(second):
|
|
first, second = action1(stream, out, first, second)
|
|
elif second == ' ':
|
|
first, second = action3(stream, out, first, second)
|
|
elif id_literal(second):
|
|
first, second = action1(stream, out, first, second)
|
|
else:
|
|
first, second = action2(stream, out, first, second)
|
|
elif second == ' ':
|
|
if id_literal(first):
|
|
first, second = action1(stream, out, first, second)
|
|
else:
|
|
first, second = action3(stream, out, first, second)
|
|
elif second == '\n':
|
|
if close_match(first):
|
|
first, second = action1(stream, out, first, second)
|
|
elif id_literal(first):
|
|
first, second = action1(stream, out, first, second)
|
|
else:
|
|
first, second = action3(stream, out, first, second)
|
|
else:
|
|
first, second = action1(stream, out, first, second)
|
|
except EOFError:
|
|
pass # done.
|
|
|
|
|
|
def jsmin(script):
|
|
"""
|
|
Minify JS
|
|
|
|
:Parameters:
|
|
`script` : ``str``
|
|
JS to minify
|
|
|
|
:Return: Minified JS
|
|
:Rtype: ``str``
|
|
"""
|
|
out = _string_io.StringIO()
|
|
jsmin_stream(_string_io.StringIO(script), out)
|
|
return out.getvalue().strip()
|
|
|
|
|
|
if __name__ == '__main__':
|
|
import sys as _sys
|
|
_sys.stdout.write(jsmin(_sys.stdin.read()))
|