home *** CD-ROM | disk | FTP | other *** search
/ Maximum CD 2010 July / maximum-cd-2010-07.iso / DiscContents / wesnoth-1.8-win32.exe / data / tools / wmlvalidator < prev    next >
Encoding:
Text File  |  2010-03-08  |  6.4 KB  |  168 lines

  1. #!/usr/bin/env python
  2. """
  3. wmltest -- tool to validate the syntax and semantics of WML.
  4.  
  5. Use --help to see usage.
  6. """
  7. #TODO:
  8. #-define verbosity levels better
  9.  
  10. import wesnoth.wmldata as wmldata
  11. import wesnoth.wmlparser as wmlparser
  12. import wesnoth.wmlgrammar as wmlgrammar
  13. import re
  14.  
  15. def print_indent(string, depth, char=' '):
  16.     print "%s%s" % (depth * char, string)
  17.  
  18. class Validator:
  19.     """
  20.     The class that takes a wmlgrammer object to validate wml trees with
  21.     """
  22.     def __init__(self, schema, verbosity=0):
  23.         self.schema = wmlgrammar.Grammar(schema)
  24.         self.verbosity = verbosity
  25.  
  26.     def validate(self, node, depth=0, name=None):
  27.         """
  28.         Validate the given DataSub node.
  29.         depth indicates how deep we've recursed into the tree,
  30.         name is a mechanism for overwriting the node name to look up in the schema, used for overloaded names.
  31.         """
  32.         if not name or name == node.name:
  33.             name = node.name
  34.             verbosename = name
  35.         else:
  36.             verbosename = "%s (%s)" % (node.name, name)
  37.  
  38.         if self.verbosity > 1:
  39.             print_indent(node.name, depth)
  40.  
  41.         try:
  42.             schema = self.schema.get_element(name)
  43.         except KeyError:
  44.             print "No valid schema found for %s" % verbosename
  45.             return
  46.  
  47.  
  48.         # TODO: the blocks below probably need to be rewritten
  49.  
  50.         # Validate the attributes
  51.         for attribute in schema.get_attributes():
  52.             matches = node.get_texts(attribute.name)
  53.             nummatches = len(matches)
  54.             if attribute.freq == wmlgrammar.REQUIRED and nummatches != 1:
  55.                 print "(%s:%d) Attribute '[%s] %s' should appear exactly once, not %d times" % (node.file, node.line, verbosename, attribute.name, nummatches)
  56.             elif attribute.freq == wmlgrammar.OPTIONAL and nummatches > 1:
  57.                 print "(%s:%d) Attribute '[%s] %s' should appear at most once, not %d times" % (node.file, node.line, verbosename, attribute.name, nummatches)
  58.             elif attribute.freq == wmlgrammar.FORBIDDEN and nummatches > 0:
  59.                 print "(%s:%d) Attribute '[%s] %s' should not appear. It appears %d times" % (node.file, node.line, verbosename, attribute.name, nummatches)
  60.             for match in matches:
  61.                 if not attribute.validate(match.data):
  62.                     print "(%s:%d) Attribute '[%s] %s's value should be %s, found: %s" % (node.file, node.line, verbosename, attribute.name, attribute.type, match.data)
  63.                 node.remove(match) # Get rid of these so we can see what's left
  64.         for attribute in node.get_all_text():
  65.             print "(%s:%d) Attribute '[%s] %s' found, which has no meaning there" % (node.file, node.line, verbosename, attribute.name)
  66.  
  67.         # Validate the elements
  68.         for element in schema.get_elements():
  69.             matches = node.get_subs(element.name)
  70.             nummatches = len(matches)
  71.             if element.freq == wmlgrammar.REQUIRED and nummatches != 1:
  72.                 print "(%s:%d) Element '[%s] [%s]' should appear exactly once, not %d times" % (node.file, node.line, verbosename, element.name, nummatches)
  73.             elif element.freq == wmlgrammar.OPTIONAL and nummatches > 1:
  74.                 print "(%s:%d) Element '[%s] [%s]' should appear at most once, not %d times" % (node.file, node.line, verbosename, element.name, nummatches)
  75.             for match in matches:
  76.                 self.validate(match, depth+1, element.subname)
  77.                 node.remove(match)
  78.  
  79.         for element in node.get_all_subs():
  80.             print "(%s:%d) Element '[%s] [%s]' found, which has no meaning there" % (node.file, node.line, verbosename, element.name)
  81.             # Do we want to do this?
  82.             if False:
  83.                 print "Attempting to validate [%s] anyway" % element.name
  84.                 self.validate(element, depth+1)
  85.  
  86. if __name__ == '__main__':
  87.     import optparse, subprocess, os, codecs, sys
  88.  
  89.     # Ugly hack to force the output of UTF-8.
  90.     # This prevents us from crashing when we're being verbose
  91.     #  and encounter a non-ascii character.
  92.     sys.stdout = codecs.getwriter('utf-8')(sys.stdout)
  93.  
  94.     op = optparse.OptionParser()
  95.     op.set_usage("Usage: %prog [options] [filename]")
  96.     op.add_option("-p", "--path",
  97.         help = "Specify Wesnoth's data dir",
  98.         dest = "path")
  99.     op.add_option("-u", "--userpath",
  100.         help = "Specify user data dir",
  101.         dest = "userpath")
  102.     op.add_option("-s", "--schema",
  103.         help = "Specify WML schema",
  104.         dest = "schema")
  105.     op.add_option("-v", "--verbose",
  106.         action = "count",
  107.         dest = "verbose",
  108.         help = "Increase verbosity, 4 is the maximum.")
  109.     op.add_option("-D", "--define",
  110.         action = "append",
  111.         dest = "defines",
  112.         default = [],
  113.         help = "Define (empty) preprocessor macros, for campaign/multiplayer inclusion.")
  114.     (options, args) = op.parse_args()
  115.  
  116.     if not options.path:
  117.         try:
  118.             p = subprocess.Popen(["wesnoth", "--path"], stdout = subprocess.PIPE)
  119.             path = p.stdout.read().strip()
  120.             options.path = os.path.join(path, "data")
  121.             sys.stderr.write("No Wesnoth path given.\nAutomatically found '%s'\n" % (options.path, ) )
  122.         except OSError:
  123.             options.path = '.'
  124.             sys.stderr.write("Could not determine Wesnoth path.\nAssuming '%s'\n" % (options.path, ) )
  125.  
  126.     if len(args) < 1:
  127.         args.append(os.path.join(options.path, '_main.cfg'))
  128.  
  129.     if options.verbose > 1:
  130.         print "Options: %s\nArgs: %s\n"% (options, args)
  131.  
  132.     if not options.schema:
  133.         options.schema = os.path.join(options.path, 'schema.cfg')
  134.  
  135.     # Parse the schema
  136.     parser = wmlparser.Parser(options.path)
  137.  
  138.     if options.verbose > 3:
  139.         parser.verbose = True
  140.     parser.parse_file(options.schema)
  141.  
  142.     schema = wmldata.DataSub("schema")
  143.     parser.parse_top(schema)
  144.  
  145.     # Construct the validator
  146.     validator = Validator(schema, options.verbose)
  147.  
  148.     # Parse the WML
  149.     parser = wmlparser.Parser(options.path, options.userpath)
  150.  
  151.     if options.verbose > 3:
  152.         parser.verbose = True
  153.  
  154.     if options.userpath:
  155.         parser.parse_text("{~add-ons}")
  156.     for file in args:
  157.         parser.parse_file(file)
  158.     for macro in options.defines:
  159.         parser.parse_text("#define %s \n#enddef" % (macro, ) )
  160.  
  161.     data = wmldata.DataSub("root")
  162.     parser.parse_top(data)
  163.  
  164.     # Validate
  165.     validator.validate(data)
  166.  
  167. # vim: tabstop=4: shiftwidth=4: expandtab: softtabstop=4: autoindent:
  168.