Not sure how useful this will be but here is a Python script that will update
rust source code to a newer flavor of rust. Unfortunately it's for a rust from
Sep 1 and it seems like there have even more syntax changes in the interval.
The script renames keywords (e.g. alt to match), some types (comm::chan to
comm:Chan), and macros (#fmt[…] to fmt!()).
-- Jesse
#!/usr/bin/python
import os, re, sys, traceback
try:
import argparse
except:
sys.stderr.write("This script requires Python 2.7 or later\n")
sys.exit(2)
# key is a regex to be match. Note that this will not match within strings or
comments.
# value will replace the first group matched. It may be a string optionally
containing back references (e.g. \2 for the second group matched)
# or a function taking a match object and returning a string.
code_replacements = {
re.compile(r"(?<=\W)alt(?=\W)"): "match",
re.compile(r"(?<=\W)ret(?=\W)"): "return",
re.compile(r"(?<=\W)class(?=\W)"): "struct",
re.compile(r"(?<=\W)iface(?=\W)"): "trait",
re.compile(r"(?<=\W)import(?=\W)"): "use",
re.compile(r"(?<=\W)none(?=\W)"): "None",
re.compile(r"(?<=\W)some(?=\W)"): "Some",
re.compile(r"(?<=\W)comm::chan(?=\W)"): "comm::Chan",
re.compile(r"(?<=\W)comm::port(?=\W)"): "comm::Port",
re.compile(r"(?<=\W)mustache::str(?=\W)"): "mustache::Str",
re.compile(r"(?<=\W)mustache::bool(?=\W)"):"mustache::Bool",
re.compile(r"(?<=\W)mustache::vec(?=\W)"): "mustache::Vec",
re.compile(r"(?<=\W)mustache::map(?=\W)"):"mustache::Map",
re.compile(r"(?<=\W)mustache::fun(?=\W)"): "mustache::Fun",
re.compile(r"(?<=\W)option::option(?=\W)"): "option::Option",
re.compile(r"(?<=\W)result::ok(?=\W)"): "result::Ok", # can't be
too aggressive with these because err in particular doesn't always mean
result::Err
re.compile(r"(?<=\W)result::err(?=\W)"): "result::Err",
re.compile(r"(?<=\W)result::result(?=\W)"): "result::Result",
re.compile(r"(?<=\W)either::either(?=\W)"): "either::Either",
re.compile(r"(?<=\W)left(?=\W)"): "Left",
re.compile(r"(?<=\W)right(?=\W)"): "Right",
re.compile(r"(?<=\W)dvec<(?=\W)"): "DVec<",
re.compile(r"(?<=\W)dvec\((?=\W)"): "DVec(",
re.compile(r"(?<=\W)io::append(?=\W)"): "io::Append",
re.compile(r"(?<=\W)io::create(?=\W)"): "io::Create",
re.compile(r"(?<=\W)io::truncate(?=\W)"): "io::Truncate",
re.compile(r"(?<=\W)io::no_flag(?=\W)"): "io::NoFlag",
re.compile(r"(?<=\W)json::json(?=\W)"): "json::Json",
re.compile(r"(?<=\W)json::num(?=\W)"): "json::Num",
re.compile(r"(?<=\W)json::string(?=\W)"): "json::String",
re.compile(r"(?<=\W)json::boolean(?=\W)"): "json::Boolean",
re.compile(r"(?<=\W)json::list(?=\W)"): "json::List",
re.compile(r"(?<=\W)json::dict(?=\W)"): "json::Dict",
re.compile(r"(?<=\W)json::null(?=\W)"): "json::Null",
re.compile(r"(?<=\W)option<(?=\W)"): "Option<",
re.compile(r"(?<=\W)reader_util(?=\W)"): "ReaderUtil",
re.compile(r"(?<=\W)writer_util(?=\W)"): "WriterUtil",
re.compile(r"(?<=\W)timespec(?=\W)"): "Timespec",
re.compile(r"(?<=\W)tm(?=\W)"): "Tm",
re.compile(r"(?<=\W)with(?=\W)"): ", ..",
# (impl) of? (some_trait) for (some_type)
re.compile(r"(impl \s+ (?: <[^>]+>)?) \s* (?: \w+ \s+)? (?: of \s+)? (\w+)
\s+ for \s+ ([\w@~[\]<>,\\ ()\-&]+)", re.VERBOSE): r"\1 \3 : \2 ",
}
code_warnings = {
re.compile(r"(?: enum \s+) ([a-z][\w:]*)", re.VERBOSE): r"enum \1 is not
CamelCase",
re.compile(r"(?: trait \s+) ([a-z][\w:]*)", re.VERBOSE): r"trait \1
is not CamelCase",
re.compile(r"(?: impl \s+) ([a-z][\w:]*)", re.VERBOSE): r"impl \1 is
not CamelCase",
re.compile(r"(?: struct \s+) ([a-z][\w:]*)", re.VERBOSE): r"struct \1 is
not CamelCase",
}
# Like code_replacements except that the keys match the entire content.
full_replacements = {
re.compile(r"\#(\w+)\[(.*?)\]"): r"\1!(\2)",
re.compile(r"\#(\w+)\((.*?)\)"): r"\1!(\2)",
}
args = None
not_camel_cased = 0
# comment | comment |
string | char (need to match this to avoid stuff
like '"' from hosing us)
not_code = re.compile(r'''(// .* $) | (/\* (?: . | \r | \n)*? \*/) | ("
([^"\\] | \\. | \\\n | \\\r)* ") | (' ([^'\\] | \\.)* ')''', re.MULTILINE |
re.VERBOSE)
# TODO:
# somehow add fat arrows to match arms
# warn if `use std;` is first thing in a crate (needs to be after crate
attributes)
class Process(object):
def __init__(self, path):
self.__path = path
def process(self):
if args.verbose >= 3:
print "processing %s" % self.__path
try:
result = ""
contents = self.__read_file()
contents = self.__process_all(contents)
for begin, end, is_code in self.__code_ranges(contents):
text = contents[begin:end]
if args.verbose >= 4:
print "%s:%s %s" % (begin, end, text)
if is_code:
result += self.__process_code(text)
else:
result += text
if result != contents:
with open(self.__path, 'w') as f:
f.write(result)
except Exception, e:
print "Failed to process %s: %s" % (self.__path, e)
traceback.print_exc(file=sys.stdout)
def __process_all(self, contents):
for key, value in full_replacements.items():
contents = key.sub(value, contents)
return contents
def __process_code(self, code):
for key, value in code_replacements.items():
code = key.sub(value, code)
for key, value in code_warnings.items():
match = key.search(code)
if match:
warning = match.expand(value)
if 'CamelCase' in warning:
global not_camel_cased
not_camel_cased += 1
else:
print warning
return code
# Generator that returns (begin, end, is_code) indexes for the code regions
in the file.
# Where code is defined as everything not a comment and not a string.
def __code_ranges(self, contents):
begin = 0
while begin < len(contents):
match = not_code.search(contents, begin)
if match:
end = match.start()
yield (begin, end, True)
begin = end
end = match.end()
yield (begin, end, False)
else:
end = len(contents)
yield (begin, end, True)
begin = end
def __read_file(self):
with open(self.__path, 'r') as f:
contents = f.read()
return contents
parser = argparse.ArgumentParser(description = "Update *.rs and *.rc files to
match rust 0.4 syntax.")
parser.add_argument("--verbose", "-v", action='count', help = 'print extra
information')
parser.add_argument("root", metavar = "ITEM", help = "path to a rust file file
or a directory")
args = parser.parse_args()
if os.path.isfile(args.root):
if os.path.splitext(args.root)[1] == '.rs':
process = Process(args.root)
process.process()
else:
print '%s is not a rust file' % args.root
sys.exit(1)
elif os.path.isdir(args.root):
for root, dirs, files in os.walk(args.root):
for name in files:
if os.path.splitext(name)[1] == '.rs':
path = os.path.join(root, name)
process = Process(path)
process.process()
else:
print "%s does not point to a file or a directory" % args.root
sys.exit(1)
if not_camel_cased > 0:
print "There were %s types not CamelCased: use
`#[warn(non_camel_case_types)]` to find them." % not_camel_cased
_______________________________________________
Rust-dev mailing list
[email protected]
https://mail.mozilla.org/listinfo/rust-dev