rust/src/etc/2014-06-rewrite-bytes-macros.py

139 lines
3.5 KiB
Python
Raw Normal View History

#!/bin/env python
#
# Copyright 2014 The Rust Project Developers. See the COPYRIGHT
# file at the top-level directory of this distribution and at
# http://rust-lang.org/COPYRIGHT.
#
# Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
# http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
# <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
# option. This file may not be copied, modified, or distributed
# except according to those terms.
import sys
import subprocess
import re
def main():
if len(sys.argv) <= 1:
print('Usage: %s [ --apply ] filename1.rs filename2.rs ...'
% sys.argv[0])
elif sys.argv[1] == '--apply':
for filename in sys.argv[2:]:
patch(filename)
else:
for filename in sys.argv[1:]:
diff(filename)
def patch(filename):
source = read(filename)
rewritten = rewrite_bytes_macros(source)
if rewritten is not None and rewritten != source:
write(filename, rewritten)
def diff(filename):
rewritten = rewrite_bytes_macros(read(filename))
if rewritten is not None:
p = subprocess.Popen(['diff', '-u', filename, '-'],
stdin=subprocess.PIPE)
p.stdin.write(rewritten)
p.stdin.close()
p.wait()
def read(filename):
with open(filename, 'rb') as f:
return f.read()
def write(filename, content):
with open(filename, 'wb') as f:
f.write(content)
def rewrite_bytes_macros(source):
rewritten, num_occurrences = BYTES_MACRO_RE.subn(rewrite_one_macro, source)
if num_occurrences > 0:
return rewritten
BYTES_MACRO_RE = re.compile(br'bytes!\( (?P<args> [^)]* ) \)', re.VERBOSE)
def rewrite_one_macro(match):
try:
bytes = parse_bytes(split_args(match.group('args')))
return b'b"' + b''.join(map(escape, bytes)) + b'"'
except SkipThisRewrite:
print('Skipped: %s' % match.group(0).decode('utf8', 'replace'))
return match.group(0)
class SkipThisRewrite(Exception):
pass
def split_args(args):
previous = b''
for arg in args.split(b','):
if previous:
arg = previous + b',' + arg
if arg.count(b'"') % 2 == 0:
yield arg
previous = b''
else:
previous = arg
if previous:
yield previous
def parse_bytes(args):
for arg in args:
arg = arg.strip()
if (arg.startswith(b'"') and arg.endswith(b'"')) or (
arg.startswith(b"'") and arg.endswith(b"'")):
# Escaped newline means something different in Rust and Python.
if b'\\\n' in arg:
raise SkipThisRewrite
for byte in eval(b'u' + arg).encode('utf8'):
yield ord(byte)
else:
if arg.endswith(b'u8'):
arg = arg[:-2]
# Assume that all Rust integer literals
# are valid Python integer literals
value = int(eval(arg))
assert value <= 0xFF
yield value
def escape(byte):
c = chr(byte)
escaped = {
b'\0': br'\0',
b'\t': br'\t',
b'\n': br'\n',
b'\r': br'\r',
b'\'': b'\\\'',
b'\\': br'\\',
}.get(c)
if escaped is not None:
return escaped
elif b' ' <= c <= b'~':
return chr(byte)
else:
return ('\\x%02X' % byte).encode('ascii')
if str is not bytes:
# Python 3.x
ord = lambda x: x
chr = lambda x: bytes([x])
if __name__ == '__main__':
main()