summaryrefslogtreecommitdiff
path: root/data/xacro_standalone.py
diff options
context:
space:
mode:
Diffstat (limited to 'data/xacro_standalone.py')
-rwxr-xr-xdata/xacro_standalone.py1087
1 files changed, 551 insertions, 536 deletions
diff --git a/data/xacro_standalone.py b/data/xacro_standalone.py
index a1e77f6f9..384c91bf6 100755
--- a/data/xacro_standalone.py
+++ b/data/xacro_standalone.py
@@ -44,157 +44,162 @@ import xml
from xml.dom.minidom import parse
try:
- _basestr = basestring
+ _basestr = basestring
except NameError:
- _basestr = str
+ _basestr = str
# Dictionary of subtitution args
substitution_args_context = {}
class XacroException(Exception):
- pass
+ pass
def isnumber(x):
- return hasattr(x, '__int__')
+ return hasattr(x, '__int__')
# Better pretty printing of xml
# Taken from http://ronrothman.com/public/leftbraned/xml-dom-minidom-toprettyxml-and-silly-whitespace/
def fixed_writexml(self, writer, indent="", addindent="", newl=""):
- # indent = current indentation
- # addindent = indentation to add to higher levels
- # newl = newline string
- writer.write(indent + "<" + self.tagName)
-
- attrs = self._get_attributes()
- a_names = list(attrs.keys())
- a_names.sort()
-
- for a_name in a_names:
- writer.write(" %s=\"" % a_name)
- xml.dom.minidom._write_data(writer, attrs[a_name].value)
- writer.write("\"")
- if self.childNodes:
- if len(self.childNodes) == 1 \
- and self.childNodes[0].nodeType == xml.dom.minidom.Node.TEXT_NODE:
- writer.write(">")
- self.childNodes[0].writexml(writer, "", "", "")
- writer.write("</%s>%s" % (self.tagName, newl))
- return
- writer.write(">%s" % (newl))
- for node in self.childNodes:
- # skip whitespace-only text nodes
- if node.nodeType == xml.dom.minidom.Node.TEXT_NODE and \
- not node.data.strip():
- continue
- node.writexml(writer, indent + addindent, addindent, newl)
- writer.write("%s</%s>%s" % (indent, self.tagName, newl))
- else:
- writer.write("/>%s" % (newl))
+ # indent = current indentation
+ # addindent = indentation to add to higher levels
+ # newl = newline string
+ writer.write(indent + "<" + self.tagName)
+
+ attrs = self._get_attributes()
+ a_names = list(attrs.keys())
+ a_names.sort()
+
+ for a_name in a_names:
+ writer.write(" %s=\"" % a_name)
+ xml.dom.minidom._write_data(writer, attrs[a_name].value)
+ writer.write("\"")
+ if self.childNodes:
+ if len(self.childNodes) == 1 \
+ and self.childNodes[0].nodeType == xml.dom.minidom.Node.TEXT_NODE:
+ writer.write(">")
+ self.childNodes[0].writexml(writer, "", "", "")
+ writer.write("</%s>%s" % (self.tagName, newl))
+ return
+ writer.write(">%s" % (newl))
+ for node in self.childNodes:
+ # skip whitespace-only text nodes
+ if node.nodeType == xml.dom.minidom.Node.TEXT_NODE and \
+ not node.data.strip():
+ continue
+ node.writexml(writer, indent + addindent, addindent, newl)
+ writer.write("%s</%s>%s" % (indent, self.tagName, newl))
+ else:
+ writer.write("/>%s" % (newl))
+
+
# replace minidom's function with ours
xml.dom.minidom.Element.writexml = fixed_writexml
class Table:
- def __init__(self, parent=None):
- self.parent = parent
- self.table = {}
-
- def __getitem__(self, key):
- if key in self.table:
- return self.table[key]
- elif self.parent:
- return self.parent[key]
- else:
- raise KeyError(key)
- def __setitem__(self, key, value):
- self.table[key] = value
+ def __init__(self, parent=None):
+ self.parent = parent
+ self.table = {}
- def __contains__(self, key):
- return \
- key in self.table or \
- (self.parent and key in self.parent)
+ def __getitem__(self, key):
+ if key in self.table:
+ return self.table[key]
+ elif self.parent:
+ return self.parent[key]
+ else:
+ raise KeyError(key)
+
+ def __setitem__(self, key, value):
+ self.table[key] = value
+
+ def __contains__(self, key):
+ return \
+ key in self.table or \
+ (self.parent and key in self.parent)
class QuickLexer(object):
- def __init__(self, **res):
- self.str = ""
- self.top = None
- self.res = []
- for k, v in res.items():
- self.__setattr__(k, len(self.res))
- self.res.append(v)
-
- def lex(self, str):
- self.str = str
- self.top = None
- self.next()
-
- def peek(self):
- return self.top
-
- def next(self):
- result = self.top
- self.top = None
- for i in range(len(self.res)):
- m = re.match(self.res[i], self.str)
- if m:
- self.top = (i, m.group(0))
- self.str = self.str[m.end():]
- break
- return result
+
+ def __init__(self, **res):
+ self.str = ""
+ self.top = None
+ self.res = []
+ for k, v in res.items():
+ self.__setattr__(k, len(self.res))
+ self.res.append(v)
+
+ def lex(self, str):
+ self.str = str
+ self.top = None
+ self.next()
+
+ def peek(self):
+ return self.top
+
+ def next(self):
+ result = self.top
+ self.top = None
+ for i in range(len(self.res)):
+ m = re.match(self.res[i], self.str)
+ if m:
+ self.top = (i, m.group(0))
+ self.str = self.str[m.end():]
+ break
+ return result
def first_child_element(elt):
- c = elt.firstChild
- while c:
- if c.nodeType == xml.dom.Node.ELEMENT_NODE:
- return c
- c = c.nextSibling
- return None
+ c = elt.firstChild
+ while c:
+ if c.nodeType == xml.dom.Node.ELEMENT_NODE:
+ return c
+ c = c.nextSibling
+ return None
def next_sibling_element(elt):
- c = elt.nextSibling
- while c:
- if c.nodeType == xml.dom.Node.ELEMENT_NODE:
- return c
- c = c.nextSibling
- return None
+ c = elt.nextSibling
+ while c:
+ if c.nodeType == xml.dom.Node.ELEMENT_NODE:
+ return c
+ c = c.nextSibling
+ return None
# Pre-order traversal of the elements
def next_element(elt):
- child = first_child_element(elt)
- if child:
- return child
- while elt and elt.nodeType == xml.dom.Node.ELEMENT_NODE:
- next = next_sibling_element(elt)
- if next:
- return next
- elt = elt.parentNode
- return None
+ child = first_child_element(elt)
+ if child:
+ return child
+ while elt and elt.nodeType == xml.dom.Node.ELEMENT_NODE:
+ next = next_sibling_element(elt)
+ if next:
+ return next
+ elt = elt.parentNode
+ return None
# Pre-order traversal of all the nodes
def next_node(node):
- if node.firstChild:
- return node.firstChild
- while node:
- if node.nextSibling:
- return node.nextSibling
- node = node.parentNode
- return None
+ if node.firstChild:
+ return node.firstChild
+ while node:
+ if node.nextSibling:
+ return node.nextSibling
+ node = node.parentNode
+ return None
def child_nodes(elt):
- c = elt.firstChild
- while c:
- yield c
- c = c.nextSibling
+ c = elt.firstChild
+ while c:
+ yield c
+ c = c.nextSibling
+
all_includes = []
@@ -210,496 +215,506 @@ include_no_matches_msg = """Include tag filename spec \"{}\" matched no files.""
## @throws XacroException if a parsing error occurs with an included document
def process_includes(doc, base_dir):
- namespaces = {}
- previous = doc.documentElement
- elt = next_element(previous)
- while elt:
- # Xacro should not use plain 'include' tags but only namespaced ones. Causes conflicts with
- # other XML elements including Gazebo's <gazebo> extensions
- is_include = False
- if elt.tagName == 'xacro:include' or elt.tagName == 'include':
-
- is_include = True
- # Temporary fix for ROS Hydro and the xacro include scope problem
- if elt.tagName == 'include':
-
- # check if there is any element within the <include> tag. mostly we are concerned
- # with Gazebo's <uri> element, but it could be anything. also, make sure the child
- # nodes aren't just a single Text node, which is still considered a deprecated
- # instance
- if elt.childNodes and not (len(elt.childNodes) == 1 and
- elt.childNodes[0].nodeType == elt.TEXT_NODE):
- # this is not intended to be a xacro element, so we can ignore it
- is_include = False
- else:
- # throw a deprecated warning
- print(deprecated_include_msg, file=sys.stderr)
-
- # Process current element depending on previous conditions
- if is_include:
- filename_spec = eval_text(elt.getAttribute('filename'), {})
- if not os.path.isabs(filename_spec):
- filename_spec = os.path.join(base_dir, filename_spec)
-
- if re.search('[*[?]+', filename_spec):
- # Globbing behaviour
- filenames = sorted(glob.glob(filename_spec))
- if len(filenames) == 0:
- print(include_no_matches_msg.format(filename_spec), file=sys.stderr)
- else:
- # Default behaviour
- filenames = [filename_spec]
-
- for filename in filenames:
- global all_includes
- all_includes.append(filename)
- try:
- with open(filename) as f:
- try:
- included = parse(f)
- except Exception as e:
- raise XacroException(
- "included file \"%s\" generated an error during XML parsing: %s"
- % (filename, str(e)))
- except IOError as e:
- raise XacroException("included file \"%s\" could not be opened: %s" % (filename, str(e)))
-
- # Replaces the include tag with the elements of the included file
- for c in child_nodes(included.documentElement):
- elt.parentNode.insertBefore(c.cloneNode(deep=True), elt)
-
- # Grabs all the declared namespaces of the included document
- for name, value in included.documentElement.attributes.items():
- if name.startswith('xmlns:'):
- namespaces[name] = value
-
- elt.parentNode.removeChild(elt)
- elt = None
+ namespaces = {}
+ previous = doc.documentElement
+ elt = next_element(previous)
+ while elt:
+ # Xacro should not use plain 'include' tags but only namespaced ones. Causes conflicts with
+ # other XML elements including Gazebo's <gazebo> extensions
+ is_include = False
+ if elt.tagName == 'xacro:include' or elt.tagName == 'include':
+
+ is_include = True
+ # Temporary fix for ROS Hydro and the xacro include scope problem
+ if elt.tagName == 'include':
+
+ # check if there is any element within the <include> tag. mostly we are concerned
+ # with Gazebo's <uri> element, but it could be anything. also, make sure the child
+ # nodes aren't just a single Text node, which is still considered a deprecated
+ # instance
+ if elt.childNodes and not (len(elt.childNodes) == 1 and
+ elt.childNodes[0].nodeType == elt.TEXT_NODE):
+ # this is not intended to be a xacro element, so we can ignore it
+ is_include = False
else:
- previous = elt
+ # throw a deprecated warning
+ print(deprecated_include_msg, file=sys.stderr)
+
+ # Process current element depending on previous conditions
+ if is_include:
+ filename_spec = eval_text(elt.getAttribute('filename'), {})
+ if not os.path.isabs(filename_spec):
+ filename_spec = os.path.join(base_dir, filename_spec)
+
+ if re.search('[*[?]+', filename_spec):
+ # Globbing behaviour
+ filenames = sorted(glob.glob(filename_spec))
+ if len(filenames) == 0:
+ print(include_no_matches_msg.format(filename_spec), file=sys.stderr)
+ else:
+ # Default behaviour
+ filenames = [filename_spec]
+
+ for filename in filenames:
+ global all_includes
+ all_includes.append(filename)
+ try:
+ with open(filename) as f:
+ try:
+ included = parse(f)
+ except Exception as e:
+ raise XacroException(
+ "included file \"%s\" generated an error during XML parsing: %s" %
+ (filename, str(e)))
+ except IOError as e:
+ raise XacroException("included file \"%s\" could not be opened: %s" % (filename, str(e)))
+
+ # Replaces the include tag with the elements of the included file
+ for c in child_nodes(included.documentElement):
+ elt.parentNode.insertBefore(c.cloneNode(deep=True), elt)
+
+ # Grabs all the declared namespaces of the included document
+ for name, value in included.documentElement.attributes.items():
+ if name.startswith('xmlns:'):
+ namespaces[name] = value
+
+ elt.parentNode.removeChild(elt)
+ elt = None
+ else:
+ previous = elt
- elt = next_element(previous)
+ elt = next_element(previous)
- # Makes sure the final document declares all the namespaces of the included documents.
- for k, v in namespaces.items():
- doc.documentElement.setAttribute(k, v)
+ # Makes sure the final document declares all the namespaces of the included documents.
+ for k, v in namespaces.items():
+ doc.documentElement.setAttribute(k, v)
# Returns a dictionary: { macro_name => macro_xml_block }
def grab_macros(doc):
- macros = {}
+ macros = {}
- previous = doc.documentElement
- elt = next_element(previous)
- while elt:
- if elt.tagName == 'macro' or elt.tagName == 'xacro:macro':
- name = elt.getAttribute('name')
+ previous = doc.documentElement
+ elt = next_element(previous)
+ while elt:
+ if elt.tagName == 'macro' or elt.tagName == 'xacro:macro':
+ name = elt.getAttribute('name')
- macros[name] = elt
- macros['xacro:' + name] = elt
+ macros[name] = elt
+ macros['xacro:' + name] = elt
- elt.parentNode.removeChild(elt)
- elt = None
- else:
- previous = elt
+ elt.parentNode.removeChild(elt)
+ elt = None
+ else:
+ previous = elt
- elt = next_element(previous)
- return macros
+ elt = next_element(previous)
+ return macros
# Returns a Table of the properties
def grab_properties(doc):
- table = Table()
+ table = Table()
+
+ previous = doc.documentElement
+ elt = next_element(previous)
+ while elt:
+ if elt.tagName == 'property' or elt.tagName == 'xacro:property':
+ name = elt.getAttribute('name')
+ value = None
+
+ if elt.hasAttribute('value'):
+ value = elt.getAttribute('value')
+ else:
+ name = '**' + name
+ value = elt # debug
+
+ bad = string.whitespace + "${}"
+ has_bad = False
+ for b in bad:
+ if b in name:
+ has_bad = True
+ break
+
+ if has_bad:
+ sys.stderr.write('Property names may not have whitespace, ' + '"{", "}", or "$" : "' +
+ name + '"')
+ else:
+ table[name] = value
+
+ elt.parentNode.removeChild(elt)
+ elt = None
+ else:
+ previous = elt
- previous = doc.documentElement
elt = next_element(previous)
- while elt:
- if elt.tagName == 'property' or elt.tagName == 'xacro:property':
- name = elt.getAttribute('name')
- value = None
-
- if elt.hasAttribute('value'):
- value = elt.getAttribute('value')
- else:
- name = '**' + name
- value = elt # debug
-
- bad = string.whitespace + "${}"
- has_bad = False
- for b in bad:
- if b in name:
- has_bad = True
- break
-
- if has_bad:
- sys.stderr.write('Property names may not have whitespace, ' +
- '"{", "}", or "$" : "' + name + '"')
- else:
- table[name] = value
-
- elt.parentNode.removeChild(elt)
- elt = None
- else:
- previous = elt
-
- elt = next_element(previous)
- return table
+ return table
def eat_ignore(lex):
- while lex.peek() and lex.peek()[0] == lex.IGNORE:
- lex.next()
+ while lex.peek() and lex.peek()[0] == lex.IGNORE:
+ lex.next()
def eval_lit(lex, symbols):
- eat_ignore(lex)
- if lex.peek()[0] == lex.NUMBER:
- return float(lex.next()[1])
- if lex.peek()[0] == lex.SYMBOL:
- try:
- key = lex.next()[1]
- value = symbols[key]
- except KeyError as ex:
- raise XacroException("Property wasn't defined: %s" % str(ex))
- if not (isnumber(value) or isinstance(value, _basestr)):
- if value is None:
- raise XacroException("Property %s recursively used" % key)
- raise XacroException("WTF2")
- try:
- return int(value)
- except:
- try:
- return float(value)
- except:
- # prevent infinite recursion
- symbols[key] = None
- result = eval_text(value, symbols)
- # restore old entry
- symbols[key] = value
- return result
- raise XacroException("Bad literal")
+ eat_ignore(lex)
+ if lex.peek()[0] == lex.NUMBER:
+ return float(lex.next()[1])
+ if lex.peek()[0] == lex.SYMBOL:
+ try:
+ key = lex.next()[1]
+ value = symbols[key]
+ except KeyError as ex:
+ raise XacroException("Property wasn't defined: %s" % str(ex))
+ if not (isnumber(value) or isinstance(value, _basestr)):
+ if value is None:
+ raise XacroException("Property %s recursively used" % key)
+ raise XacroException("WTF2")
+ try:
+ return int(value)
+ except:
+ try:
+ return float(value)
+ except:
+ # prevent infinite recursion
+ symbols[key] = None
+ result = eval_text(value, symbols)
+ # restore old entry
+ symbols[key] = value
+ return result
+ raise XacroException("Bad literal")
def eval_factor(lex, symbols):
- eat_ignore(lex)
+ eat_ignore(lex)
- neg = 1
- if lex.peek()[1] == '-':
- lex.next()
- neg = -1
+ neg = 1
+ if lex.peek()[1] == '-':
+ lex.next()
+ neg = -1
- if lex.peek()[0] in [lex.NUMBER, lex.SYMBOL]:
- return neg * eval_lit(lex, symbols)
- if lex.peek()[0] == lex.LPAREN:
- lex.next()
- eat_ignore(lex)
- result = eval_expr(lex, symbols)
- eat_ignore(lex)
- if lex.next()[0] != lex.RPAREN:
- raise XacroException("Unmatched left paren")
- eat_ignore(lex)
- return neg * result
+ if lex.peek()[0] in [lex.NUMBER, lex.SYMBOL]:
+ return neg * eval_lit(lex, symbols)
+ if lex.peek()[0] == lex.LPAREN:
+ lex.next()
+ eat_ignore(lex)
+ result = eval_expr(lex, symbols)
+ eat_ignore(lex)
+ if lex.next()[0] != lex.RPAREN:
+ raise XacroException("Unmatched left paren")
+ eat_ignore(lex)
+ return neg * result
- raise XacroException("Misplaced operator")
+ raise XacroException("Misplaced operator")
def eval_term(lex, symbols):
+ eat_ignore(lex)
+
+ result = 0
+ if lex.peek()[0] in [lex.NUMBER, lex.SYMBOL, lex.LPAREN] \
+ or lex.peek()[1] == '-':
+ result = eval_factor(lex, symbols)
+
+ eat_ignore(lex)
+ while lex.peek() and lex.peek()[1] in ['*', '/']:
+ op = lex.next()[1]
+ n = eval_factor(lex, symbols)
+
+ if op == '*':
+ result = float(result) * float(n)
+ elif op == '/':
+ result = float(result) / float(n)
+ else:
+ raise XacroException("WTF")
eat_ignore(lex)
+ return result
- result = 0
- if lex.peek()[0] in [lex.NUMBER, lex.SYMBOL, lex.LPAREN] \
- or lex.peek()[1] == '-':
- result = eval_factor(lex, symbols)
- eat_ignore(lex)
- while lex.peek() and lex.peek()[1] in ['*', '/']:
- op = lex.next()[1]
- n = eval_factor(lex, symbols)
-
- if op == '*':
- result = float(result) * float(n)
- elif op == '/':
- result = float(result) / float(n)
- else:
- raise XacroException("WTF")
- eat_ignore(lex)
- return result
+def eval_expr(lex, symbols):
+ eat_ignore(lex)
+ op = None
+ if lex.peek()[0] == lex.OP:
+ op = lex.next()[1]
+ if not op in ['+', '-']:
+ raise XacroException("Invalid operation. Must be '+' or '-'")
-def eval_expr(lex, symbols):
- eat_ignore(lex)
+ result = eval_term(lex, symbols)
+ if op == '-':
+ result = -float(result)
- op = None
- if lex.peek()[0] == lex.OP:
- op = lex.next()[1]
- if not op in ['+', '-']:
- raise XacroException("Invalid operation. Must be '+' or '-'")
+ eat_ignore(lex)
+ while lex.peek() and lex.peek()[1] in ['+', '-']:
+ op = lex.next()[1]
+ n = eval_term(lex, symbols)
- result = eval_term(lex, symbols)
+ if op == '+':
+ result = float(result) + float(n)
if op == '-':
- result = -float(result)
-
+ result = float(result) - float(n)
eat_ignore(lex)
- while lex.peek() and lex.peek()[1] in ['+', '-']:
- op = lex.next()[1]
- n = eval_term(lex, symbols)
-
- if op == '+':
- result = float(result) + float(n)
- if op == '-':
- result = float(result) - float(n)
- eat_ignore(lex)
- return result
+ return result
def eval_text(text, symbols):
- def handle_expr(s):
- lex = QuickLexer(IGNORE=r"\s+",
- NUMBER=r"(\d+(\.\d*)?|\.\d+)([eE][-+]?\d+)?",
- SYMBOL=r"[a-zA-Z_]\w*",
- OP=r"[\+\-\*/^]",
- LPAREN=r"\(",
- RPAREN=r"\)")
- lex.lex(s)
- return eval_expr(lex, symbols)
-
- def handle_extension(s):
- return ("$(%s)" % s)
-
- results = []
- lex = QuickLexer(DOLLAR_DOLLAR_BRACE=r"\$\$+\{",
- EXPR=r"\$\{[^\}]*\}",
- EXTENSION=r"\$\([^\)]*\)",
- TEXT=r"([^\$]|\$[^{(]|\$$)+")
- lex.lex(text)
- while lex.peek():
- if lex.peek()[0] == lex.EXPR:
- results.append(handle_expr(lex.next()[1][2:-1]))
- elif lex.peek()[0] == lex.EXTENSION:
- results.append(handle_extension(lex.next()[1][2:-1]))
- elif lex.peek()[0] == lex.TEXT:
- results.append(lex.next()[1])
- elif lex.peek()[0] == lex.DOLLAR_DOLLAR_BRACE:
- results.append(lex.next()[1][1:])
- return ''.join(map(str, results))
+
+ def handle_expr(s):
+ lex = QuickLexer(IGNORE=r"\s+",
+ NUMBER=r"(\d+(\.\d*)?|\.\d+)([eE][-+]?\d+)?",
+ SYMBOL=r"[a-zA-Z_]\w*",
+ OP=r"[\+\-\*/^]",
+ LPAREN=r"\(",
+ RPAREN=r"\)")
+ lex.lex(s)
+ return eval_expr(lex, symbols)
+
+ def handle_extension(s):
+ return ("$(%s)" % s)
+
+ results = []
+ lex = QuickLexer(DOLLAR_DOLLAR_BRACE=r"\$\$+\{",
+ EXPR=r"\$\{[^\}]*\}",
+ EXTENSION=r"\$\([^\)]*\)",
+ TEXT=r"([^\$]|\$[^{(]|\$$)+")
+ lex.lex(text)
+ while lex.peek():
+ if lex.peek()[0] == lex.EXPR:
+ results.append(handle_expr(lex.next()[1][2:-1]))
+ elif lex.peek()[0] == lex.EXTENSION:
+ results.append(handle_extension(lex.next()[1][2:-1]))
+ elif lex.peek()[0] == lex.TEXT:
+ results.append(lex.next()[1])
+ elif lex.peek()[0] == lex.DOLLAR_DOLLAR_BRACE:
+ results.append(lex.next()[1][1:])
+ return ''.join(map(str, results))
# Expands macros, replaces properties, and evaluates expressions
def eval_all(root, macros, symbols):
- # Evaluates the attributes for the root node
- for at in root.attributes.items():
- result = eval_text(at[1], symbols)
- root.setAttribute(at[0], result)
-
- previous = root
- node = next_node(previous)
- while node:
- if node.nodeType == xml.dom.Node.ELEMENT_NODE:
- if node.tagName in macros:
- body = macros[node.tagName].cloneNode(deep=True)
- params = body.getAttribute('params').split()
-
- # Parse default values for any parameters
- defaultmap = {}
- for param in params[:]:
- splitParam = param.split(':=')
-
- if len(splitParam) == 2:
- defaultmap[splitParam[0]] = splitParam[1]
- params.remove(param)
- params.append(splitParam[0])
-
- elif len(splitParam) != 1:
- raise XacroException("Invalid parameter definition")
-
- # Expands the macro
- scoped = Table(symbols)
- for name, value in node.attributes.items():
- if not name in params:
- raise XacroException("Invalid parameter \"%s\" while expanding macro \"%s\"" %
- (str(name), str(node.tagName)))
- params.remove(name)
- scoped[name] = eval_text(value, symbols)
-
- # Pulls out the block arguments, in order
- cloned = node.cloneNode(deep=True)
- eval_all(cloned, macros, symbols)
- block = cloned.firstChild
- for param in params[:]:
- if param[0] == '*':
- while block and block.nodeType != xml.dom.Node.ELEMENT_NODE:
- block = block.nextSibling
- if not block:
- raise XacroException("Not enough blocks while evaluating macro %s" % str(node.tagName))
- params.remove(param)
- scoped[param] = block
- block = block.nextSibling
-
- # Try to load defaults for any remaining non-block parameters
- for param in params[:]:
- if param[0] != '*' and param in defaultmap:
- scoped[param] = defaultmap[param]
- params.remove(param)
-
- if params:
- raise XacroException("Parameters [%s] were not set for macro %s" %
- (",".join(params), str(node.tagName)))
- eval_all(body, macros, scoped)
-
- # Replaces the macro node with the expansion
- for e in list(child_nodes(body)): # Ew
- node.parentNode.insertBefore(e, node)
- node.parentNode.removeChild(node)
-
- node = None
- elif node.tagName == 'arg' or node.tagName == 'xacro:arg':
- name = node.getAttribute('name')
- if not name:
- raise XacroException("Argument name missing")
- default = node.getAttribute('default')
- if default and name not in substitution_args_context['arg']:
- substitution_args_context['arg'][name] = default
-
- node.parentNode.removeChild(node)
- node = None
-
- elif node.tagName == 'insert_block' or node.tagName == 'xacro:insert_block':
- name = node.getAttribute('name')
-
- if ("**" + name) in symbols:
- # Multi-block
- block = symbols['**' + name]
-
- for e in list(child_nodes(block)):
- node.parentNode.insertBefore(e.cloneNode(deep=True), node)
- node.parentNode.removeChild(node)
- elif ("*" + name) in symbols:
- # Single block
- block = symbols['*' + name]
-
- node.parentNode.insertBefore(block.cloneNode(deep=True), node)
- node.parentNode.removeChild(node)
- else:
- raise XacroException("Block \"%s\" was never declared" % name)
-
- node = None
- elif node.tagName in ['if', 'xacro:if', 'unless', 'xacro:unless']:
- value = eval_text(node.getAttribute('value'), symbols)
- try:
- if value == 'true': keep = True
- elif value == 'false': keep = False
- else: keep = float(value)
- except ValueError:
- raise XacroException("Xacro conditional evaluated to \"%s\". Acceptable evaluations are one of [\"1\",\"true\",\"0\",\"false\"]" % value)
- if node.tagName in ['unless', 'xacro:unless']: keep = not keep
- if keep:
- for e in list(child_nodes(node)):
- node.parentNode.insertBefore(e.cloneNode(deep=True), node)
-
- node.parentNode.removeChild(node)
- else:
- # Evals the attributes
- for at in node.attributes.items():
- result = eval_text(at[1], symbols)
- node.setAttribute(at[0], result)
- previous = node
- elif node.nodeType == xml.dom.Node.TEXT_NODE:
- node.data = eval_text(node.data, symbols)
- previous = node
+ # Evaluates the attributes for the root node
+ for at in root.attributes.items():
+ result = eval_text(at[1], symbols)
+ root.setAttribute(at[0], result)
+
+ previous = root
+ node = next_node(previous)
+ while node:
+ if node.nodeType == xml.dom.Node.ELEMENT_NODE:
+ if node.tagName in macros:
+ body = macros[node.tagName].cloneNode(deep=True)
+ params = body.getAttribute('params').split()
+
+ # Parse default values for any parameters
+ defaultmap = {}
+ for param in params[:]:
+ splitParam = param.split(':=')
+
+ if len(splitParam) == 2:
+ defaultmap[splitParam[0]] = splitParam[1]
+ params.remove(param)
+ params.append(splitParam[0])
+
+ elif len(splitParam) != 1:
+ raise XacroException("Invalid parameter definition")
+
+ # Expands the macro
+ scoped = Table(symbols)
+ for name, value in node.attributes.items():
+ if not name in params:
+ raise XacroException("Invalid parameter \"%s\" while expanding macro \"%s\"" %
+ (str(name), str(node.tagName)))
+ params.remove(name)
+ scoped[name] = eval_text(value, symbols)
+
+ # Pulls out the block arguments, in order
+ cloned = node.cloneNode(deep=True)
+ eval_all(cloned, macros, symbols)
+ block = cloned.firstChild
+ for param in params[:]:
+ if param[0] == '*':
+ while block and block.nodeType != xml.dom.Node.ELEMENT_NODE:
+ block = block.nextSibling
+ if not block:
+ raise XacroException("Not enough blocks while evaluating macro %s" %
+ str(node.tagName))
+ params.remove(param)
+ scoped[param] = block
+ block = block.nextSibling
+
+ # Try to load defaults for any remaining non-block parameters
+ for param in params[:]:
+ if param[0] != '*' and param in defaultmap:
+ scoped[param] = defaultmap[param]
+ params.remove(param)
+
+ if params:
+ raise XacroException("Parameters [%s] were not set for macro %s" %
+ (",".join(params), str(node.tagName)))
+ eval_all(body, macros, scoped)
+
+ # Replaces the macro node with the expansion
+ for e in list(child_nodes(body)): # Ew
+ node.parentNode.insertBefore(e, node)
+ node.parentNode.removeChild(node)
+
+ node = None
+ elif node.tagName == 'arg' or node.tagName == 'xacro:arg':
+ name = node.getAttribute('name')
+ if not name:
+ raise XacroException("Argument name missing")
+ default = node.getAttribute('default')
+ if default and name not in substitution_args_context['arg']:
+ substitution_args_context['arg'][name] = default
+
+ node.parentNode.removeChild(node)
+ node = None
+
+ elif node.tagName == 'insert_block' or node.tagName == 'xacro:insert_block':
+ name = node.getAttribute('name')
+
+ if ("**" + name) in symbols:
+ # Multi-block
+ block = symbols['**' + name]
+
+ for e in list(child_nodes(block)):
+ node.parentNode.insertBefore(e.cloneNode(deep=True), node)
+ node.parentNode.removeChild(node)
+ elif ("*" + name) in symbols:
+ # Single block
+ block = symbols['*' + name]
+
+ node.parentNode.insertBefore(block.cloneNode(deep=True), node)
+ node.parentNode.removeChild(node)
else:
- previous = node
+ raise XacroException("Block \"%s\" was never declared" % name)
- node = next_node(previous)
- return macros
+ node = None
+ elif node.tagName in ['if', 'xacro:if', 'unless', 'xacro:unless']:
+ value = eval_text(node.getAttribute('value'), symbols)
+ try:
+ if value == 'true': keep = True
+ elif value == 'false': keep = False
+ else: keep = float(value)
+ except ValueError:
+ raise XacroException(
+ "Xacro conditional evaluated to \"%s\". Acceptable evaluations are one of [\"1\",\"true\",\"0\",\"false\"]"
+ % value)
+ if node.tagName in ['unless', 'xacro:unless']: keep = not keep
+ if keep:
+ for e in list(child_nodes(node)):
+ node.parentNode.insertBefore(e.cloneNode(deep=True), node)
+
+ node.parentNode.removeChild(node)
+ else:
+ # Evals the attributes
+ for at in node.attributes.items():
+ result = eval_text(at[1], symbols)
+ node.setAttribute(at[0], result)
+ previous = node
+ elif node.nodeType == xml.dom.Node.TEXT_NODE:
+ node.data = eval_text(node.data, symbols)
+ previous = node
+ else:
+ previous = node
+
+ node = next_node(previous)
+ return macros
# Expands everything except includes
def eval_self_contained(doc):
- macros = grab_macros(doc)
- symbols = grab_properties(doc)
- eval_all(doc.documentElement, macros, symbols)
+ macros = grab_macros(doc)
+ symbols = grab_properties(doc)
+ eval_all(doc.documentElement, macros, symbols)
def print_usage(exit_code=0):
- print("Usage: %s [-o <output>] <input>" % 'xacro.py')
- print(" %s --deps Prints dependencies" % 'xacro.py')
- print(" %s --includes Only evalutes includes" % 'xacro.py')
- sys.exit(exit_code)
+ print("Usage: %s [-o <output>] <input>" % 'xacro.py')
+ print(" %s --deps Prints dependencies" % 'xacro.py')
+ print(" %s --includes Only evalutes includes" % 'xacro.py')
+ sys.exit(exit_code)
def set_substitution_args_context(context={}):
- substitution_args_context['arg'] = context
+ substitution_args_context['arg'] = context
+
def open_output(output_filename):
- if output_filename is None:
- return sys.stdout
- else:
- return open(output_filename, 'w')
+ if output_filename is None:
+ return sys.stdout
+ else:
+ return open(output_filename, 'w')
+
def main():
- try:
- opts, args = getopt.gnu_getopt(sys.argv[1:], "ho:", ['deps', 'includes'])
- except getopt.GetoptError as err:
- print(str(err))
- print_usage(2)
-
- just_deps = False
- just_includes = False
-
- output_filename = None
- for o, a in opts:
- if o == '-h':
- print_usage(0)
- elif o == '-o':
- output_filename = a
- elif o == '--deps':
- just_deps = True
- elif o == '--includes':
- just_includes = True
-
- if len(args) < 1:
- print("No input given")
- print_usage(2)
-
- # Process substitution args
- # set_substitution_args_context(load_mappings(sys.argv))
- set_substitution_args_context((sys.argv))
-
- f = open(args[0])
- doc = None
- try:
- doc = parse(f)
- except xml.parsers.expat.ExpatError:
- sys.stderr.write("Expat parsing error. Check that:\n")
- sys.stderr.write(" - Your XML is correctly formed\n")
- sys.stderr.write(" - You have the xacro xmlns declaration: " +
- "xmlns:xacro=\"http://www.ros.org/wiki/xacro\"\n")
- sys.stderr.write("\n")
- raise
- finally:
- f.close()
-
- process_includes(doc, os.path.dirname(args[0]))
- if just_deps:
- for inc in all_includes:
- sys.stdout.write(inc + " ")
- sys.stdout.write("\n")
- elif just_includes:
- doc.writexml(open_output(output_filename))
- print()
- else:
- eval_self_contained(doc)
- banner = [xml.dom.minidom.Comment(c) for c in
- [" %s " % ('=' * 83),
- " | This document was autogenerated by xacro from %-30s | " % args[0],
- " | EDITING THIS FILE BY HAND IS NOT RECOMMENDED %-30s | " % "",
- " %s " % ('=' * 83)]]
- first = doc.firstChild
- for comment in banner:
- doc.insertBefore(comment, first)
-
- open_output(output_filename).write(doc.toprettyxml(indent=' '))
- print()
+ try:
+ opts, args = getopt.gnu_getopt(sys.argv[1:], "ho:", ['deps', 'includes'])
+ except getopt.GetoptError as err:
+ print(str(err))
+ print_usage(2)
+
+ just_deps = False
+ just_includes = False
+
+ output_filename = None
+ for o, a in opts:
+ if o == '-h':
+ print_usage(0)
+ elif o == '-o':
+ output_filename = a
+ elif o == '--deps':
+ just_deps = True
+ elif o == '--includes':
+ just_includes = True
+
+ if len(args) < 1:
+ print("No input given")
+ print_usage(2)
+
+ # Process substitution args
+ # set_substitution_args_context(load_mappings(sys.argv))
+ set_substitution_args_context((sys.argv))
+
+ f = open(args[0])
+ doc = None
+ try:
+ doc = parse(f)
+ except xml.parsers.expat.ExpatError:
+ sys.stderr.write("Expat parsing error. Check that:\n")
+ sys.stderr.write(" - Your XML is correctly formed\n")
+ sys.stderr.write(" - You have the xacro xmlns declaration: " +
+ "xmlns:xacro=\"http://www.ros.org/wiki/xacro\"\n")
+ sys.stderr.write("\n")
+ raise
+ finally:
+ f.close()
+
+ process_includes(doc, os.path.dirname(args[0]))
+ if just_deps:
+ for inc in all_includes:
+ sys.stdout.write(inc + " ")
+ sys.stdout.write("\n")
+ elif just_includes:
+ doc.writexml(open_output(output_filename))
+ print()
+ else:
+ eval_self_contained(doc)
+ banner = [
+ xml.dom.minidom.Comment(c) for c in [
+ " %s " % ('=' * 83),
+ " | This document was autogenerated by xacro from %-30s | " % args[0],
+ " | EDITING THIS FILE BY HAND IS NOT RECOMMENDED %-30s | " % "",
+ " %s " % ('=' * 83)
+ ]
+ ]
+ first = doc.firstChild
+ for comment in banner:
+ doc.insertBefore(comment, first)
+
+ open_output(output_filename).write(doc.toprettyxml(indent=' '))
+ print()
+
if __name__ == '__main__':
- main()
+ main()