Skip to content

Commit

Permalink
Merge pull request #427 from legoktm/wmltools-cleanup
Browse files Browse the repository at this point in the history
Various Python cleanup in wmltools-related code
  • Loading branch information
Elvish-Hunter committed Jul 27, 2015
2 parents da6e149 + bd23c32 commit 1599f62
Show file tree
Hide file tree
Showing 3 changed files with 62 additions and 45 deletions.
81 changes: 48 additions & 33 deletions data/tools/wesnoth/wmlgrammar.py
@@ -1,39 +1,39 @@
"""
wmlgrammar -- parses a given schema into a more usable form
"""
import collections
import re

REQUIRED = 1
OPTIONAL = 2
REPEATED = 3
FORBIDDEN = 4


class Grammar(object):
def __init__(self, schema):
schema = schema.get_first("schema")
self.datatypes = {
"boolean" : re.compile("^(yes|no|true|false|on|off)$"),
#"character" : re.compile("^.$"),
"float" : re.compile("^(\\+|-)?[0-9]+(\.[0-9]*)?$"),
"integer" : re.compile("^(\\+|-)?[0-9]+$"),
"string" : re.compile(".*"),
"tstring" : re.compile(".*"),
"boolean": re.compile("^(yes|no|true|false|on|off)$"),
# "character" : re.compile("^.$"),
"float": re.compile("^(\\+|-)?[0-9]+(\.[0-9]*)?$"),
"integer": re.compile("^(\\+|-)?[0-9]+$"),
"string": re.compile(".*"),
"tstring": re.compile(".*"),
}
self.elements = {}
self.categories = {}
self.categories = collections.defaultdict(list)
for type in schema.get_all_text():
match = parse_match(type.data)
self.datatypes.update( { type.name : match } )
self.datatypes.update({type.name: match})
for element in schema.get_all_subs():
node = Node(element, self.datatypes)
self.elements.update( { node.name : node } )
self.elements.update({node.name: node})
for element in [el for el in self.elements.values() if el.parent]:
element.inherit(self.elements[element.parent])
# categories
for element in [el for el in self.elements.values() if el.category]:
if element.category not in self.categories:
self.categories[element.category] = []
self.categories[element.category].append( element )
self.categories[element.category].append(element)

def get_element(self, name):
return self.elements[name]
Expand All @@ -42,84 +42,98 @@ def get_datatype(self, name):
return self.datatypes[name]

def get_category(self, name):
if name not in self.categories: return []
return self.categories[name]
return self.categories.get(name, [])


class Node(object):
def __init__(self, schema, datatypes):
self.name = schema.name
self.elements = set([])
self.ext_elements = [] #Ugh, do we really want to do this?
self.attributes = set([])
self.ext_elements = [] # Ugh, do we really want to do this?
self.attributes = set()
self.parent = None
self.description = None
self.category = None
for item in schema.get_all_text():
if item.name[0] == '_':
self.elements.add( Element(item) )
self.elements.add(Element(item))
else:
self.attributes.add( Attribute(item, datatypes) )
self.attributes.add(Attribute(item, datatypes))
for item in schema.get_all_subs():
if item.name == "element":
print "[element] found in schema, not parsing yet"
#self.ext_elements...
# self.ext_elements...
elif item.name == "description":
self.description = item.get_text("text")
self.category = item.get_text("category")
else:
raise Exception( "Unknown element [%s] encountered in grammar for [%s]" % (item.name, self.name) )
raise Exception("Unknown element [%s] encountered in grammar for [%s]" % (item.name, self.name))
if ':' in self.name:
self.name, self.parent = self.name.split(':',1)
self.name, self.parent = self.name.split(':', 1)

def inherit(self, other):
assert self.parent == other.name
self.elements.update( other.elements )
self.attributes.update( other.attributes )
self.elements.update(other.elements)
self.attributes.update(other.attributes)
self.parent = None

def get_attributes(self):
return self.attributes

def get_elements(self):
return self.elements


class Element(object):
def __init__(self, schema):
first, second = schema.data.split(" ",1)
first, second = schema.data.split(" ", 1)
self.name = schema.name[1:]
self.freq = parse_frequency(first)
self.subname = second

def match(self, name):
return self.name == name

def __hash__(self):
return hash(self.name)

def __cmp__(self, other):
return (isinstance(other, type(self)) or isinstance(self, type(other))) and cmp(self.name, other.name)


class ExtElement(Element):
def __init__(self, schema):
self.re = parse_match( schema.get_text("match").data )
self.freq = parse_frequency( schema.get_text("freq").data )
self.re = parse_match(schema.get_text("match").data)
self.freq = parse_frequency(schema.get_text("freq").data)
self.subname = schema.get_text("name").data

def match(self, name):
return bool(self.re.match(name))


class Attribute(object):
def __init__(self, schema, datatypes):
first, second = schema.data.split(" ",1)
if not second in datatypes:
raise Exception( "Unknown datatype '%s'" % (second,) )
first, second = schema.data.split(" ", 1)
if second not in datatypes:
raise Exception("Unknown datatype '%s'" % second)
self.name = schema.name
self.freq = parse_frequency(first)
self.type = second
self.re = datatypes[second]

def match(self, name):
return self.name == name

def validate(self, value):
return bool(self.re.match(value))

def __hash__(self):
return hash(self.name)

def __cmp__(self, other):
return (isinstance(other, type(self)) or isinstance(self, type(other))) and cmp(self.name, other.name)


def parse_frequency(string):
if string == "required":
return REQUIRED
Expand All @@ -130,15 +144,16 @@ def parse_frequency(string):
elif string == "forbidden":
return FORBIDDEN
else:
raise Exception( "Unknown frequency '%s'" % (string,) )
raise Exception("Unknown frequency '%s'" % string)


def parse_match(string):
(matchtype, matchtext) = string.split(" ",1)
(matchtype, matchtext) = string.split(" ", 1)
if matchtype == "re":
match = re.compile(matchtext)
elif matchtype == "enum":
match = re.compile( "^(" + matchtext.replace(',','|') + ")$" )
match = re.compile("^(" + matchtext.replace(',', '|') + ")$")
else:
raise Exception( "Unknown datatype encountered in %s=\"%s\": '%s'" % (type.name, type.data, matchtype) )
raise Exception("Unknown datatype encountered in %s=\"%s\": '%s'" % (type.name, type.data, matchtype))
return match
# vim: tabstop=4: shiftwidth=4: expandtab: softtabstop=4: autoindent:
7 changes: 3 additions & 4 deletions data/tools/wesnoth/wmliterator.py
Expand Up @@ -49,14 +49,13 @@ def wmlfindin(element, scopeElement, wmlItor):
return itor
return None


def isDirective(elem):
"Identify things that shouldn't be indented."
if isinstance(elem, WmlIterator):
elem = elem.element
for prefix in ("#ifdef", "#ifndef", "#ifhave", "#ifnhave", "#ifver", "#ifnver", "#else", "#endif", "#define", "#enddef", "#undef"):
if elem.startswith(prefix):
return True
return False
return elem.startswith(("#ifdef", "#ifndef", "#ifhave", "#ifnhave", "#ifver", "#ifnver", "#else", "#endif", "#define", "#enddef", "#undef"))


def isCloser(elem):
"Are we looking at a closing tag?"
Expand Down
19 changes: 11 additions & 8 deletions data/tools/wesnoth/wmltools.py
Expand Up @@ -3,7 +3,9 @@
"""

import collections
import sys, os, re, sre_constants, hashlib, glob, gzip
import string

map_extensions = ("map", "mask")
image_extensions = ("png", "jpg", "jpeg")
Expand Down Expand Up @@ -65,7 +67,7 @@ def comma_split(csstring, list=None, strip="r"):
print 'Trailing whitespace may be problematic: "%s" in "%s"' % (item, csstring)
if 'l' not in strip:
vallist = [x.rstrip() for x in vallist]
if list != None:
if list is not None:
list.extend(vallist)
else:
return vallist
Expand Down Expand Up @@ -315,7 +317,7 @@ def actualtype(a):
atype = "terrain_code"
elif a.endswith(".wav") or a.endswith(".ogg"):
atype = "sound"
elif a.startswith('"') and a.endswith('"') or (a.startswith("_") and a[1] not in "abcdefghijklmnopqrstuvwxyz"):
elif a.startswith('"') and a.endswith('"') or (a.startswith("_") and a[1] not in string.ascii_lowercase):
atype = "stringliteral"
elif "=" in a:
atype = "filter"
Expand Down Expand Up @@ -377,16 +379,17 @@ def __init__(self, namespace, filename, lineno=None, docstring=None, args=None):
self.lineno = lineno
self.docstring = docstring
self.args = args
self.references = {}
self.references = collections.defaultdict(list)
self.undef = None

def append(self, fn, n, a=None):
if fn not in self.references:
self.references[fn] = []
self.references[fn].append((n, a))

def dump_references(self):
"Dump all known references to this definition."
for (file, refs) in self.references.items():
print " %s: %s" % (file, repr([x[0] for x in refs])[1:-1])

def __cmp__(self, other):
"Compare two documentation objects for place in the sort order."
# Major sort by file, minor by line number. This presumes that the
Expand Down Expand Up @@ -435,9 +438,9 @@ def mark_matching_resources(self, pattern, fn, n):
return key
def visible_from(self, defn, fn, n):
"Is specified definition visible from the specified file and line?"
if type(defn) == type(""):
if isinstance(defn, basestring):
defn = self.fileref[defn]
if defn.undef != None:
if defn.undef is not None:
# Local macros are only visible in the file where they were defined
return defn.filename == fn and n >= defn.lineno and n <= defn.undef
if self.exports(defn.namespace):
Expand Down Expand Up @@ -896,7 +899,7 @@ def directory_namespace(path):
def namespace_member(path, namespace):
"Is a path in a specified namespace?"
ns = directory_namespace(path)
return ns != None and ns == namespace
return ns is not None and ns == namespace

def resolve_unit_cfg(namespace, utype, resource=None):
"Get the location of a specified unit in a specified scope."
Expand Down

0 comments on commit 1599f62

Please sign in to comment.