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

Reply via email to