#!/usr/bin/env python """ wmltest -- tool to test the integrity and meaning of WML. Use --help to see usage. """ #TODO: #-Write function to check dependencies #List of dependencies to check: #-defense,movement_costs: keys should be valid terrain #-resistance: keys should be valid damage types #-effect: valid keys vary depending on name #-[attack] type: value should be valid damage types #-[filter_attack]'s keys' values import wesnoth.wmldata as wmldata import wesnoth.wmlparser as wmlparser import wesnoth.wmlgrammar as wmlgrammar import re def print_indent(string, depth, char=' '): print "%s%s" % (depth * char, string) class Tester: """ The Tester class, this walks the WML tree and checks whether stuff has meaning. (among other things) """ def __init__(self, wmltree, verbosity): self.wmltree = wmltree self.verbosity = verbosity self.grammar = wmlgrammar.Grammar().grammar() def test(self, tag=None, depth=0): """Tests whether things have meaning, walks the wml tree.""" if not tag: tag = self.wmltree if self.verbosity > 2: print_indent(tag.name, depth, ' ') for item in tag.data: if isinstance(item, wmldata.DataSub): if item.name in self.grammar[tag.name][0]: self.test(item, depth + 1) else: found = False for d in filter(lambda x:isinstance(x,dict),self.grammar[tag.name][0]): # We only check the first key in the dict, as it should only have one key = d.keys()[0] if isinstance(key, str) and key == item.name \ or isinstance(key, re._pattern_type) and key.search(item.name): found = True item.name = d[key] self.test(item, depth + 1) break # Don't recurse into the catch-all if not found: print_indent("[%s] (%s:%d) is meaningless in [%s] (%s:%d)" % (item.name, item.file, item.line, tag.name, tag.file, tag.line), depth + 1, '*') elif isinstance(item, wmldata.DataText): if item.translatable: underscore = " _ " else: underscore = "" if item.name in self.grammar[tag.name][1] or any(map(lambda x:(bool)(x.search(item.name)),filter(lambda x:isinstance(x,re._pattern_type),self.grammar[tag.name][1]))): if self.verbosity > 2: print_indent("%s=%s\"%s\"" % (item.name, underscore, item.data), depth + 1, ' ') else: print_indent("%s=%s\"%s\" (%s:%d) is meaningless in [%s] (%s:%d)" % (item.name, underscore, item.data, item.file, item.line, tag.name, tag.file, tag.line), depth + 1, '*') else: raise Exception( "Wmlparser gave us an object of class %s" % (item.__class__,) ) if __name__ == '__main__': import optparse, subprocess, os, codecs, sys # Ugly hack to force the output of UTF-8. # This prevents us from crashing when we're being verbose # and encounter a non-ascii character. sys.stdout = codecs.getwriter('utf-8')(sys.stdout) op = optparse.OptionParser() op.set_usage("Usage: %prog [options] [filename]") op.add_option("-p", "--path", help = "Specify Wesnoth's data dir.", dest = "path") op.add_option("-u", "--userpath", help = "Specify user data dir..", dest = "userpath") op.add_option("-v", "--verbose", action = "count", dest = "verbose", help = "Increase verbosity, 4 is the maximum.") op.add_option("-D", "--define", action = "append", dest = "defines", default = [], help = "Define (empty) preprocessor macros, for campaign/multiplayer inclusion.") (options, args) = op.parse_args() if not options.path: try: p = subprocess.Popen(["wesnoth", "--path"], stdout = subprocess.PIPE) path = p.stdout.read().strip() options.path = os.path.join(path, "data") sys.stderr.write("No Wesnoth path given.\nAutomatically found '%s'\n" % (options.path, ) ) except OSError: options.path = '.' sys.stderr.write("Could not determine Wesnoth path.\nAssuming '%s'\n" % (options.path, ) ) if len(args) < 1: args.append('%s/_main.cfg' % options.path) if options.verbose > 1: print "Options: %s\nArgs: %s\n"% (options, args) parser = wmlparser.Parser(options.path, options.userpath) if options.verbose > 3: parser.verbose = True if options.userpath: parser.parse_text("{~campaigns}") for file in args: parser.parse_file(file) for macro in options.defines: parser.parse_text("#define %s \n#enddef" % (macro, ) ) data = wmldata.DataSub("WML") parser.parse_top(data) tester = Tester(data, options.verbose) tester.test() # vim: tabstop=4: shiftwidth=4: expandtab: softtabstop=4: autoindent: