Package jazzparser :: Package formalisms :: Package music_halfspan :: Module domxml
[hide private]
[frames] | no frames]

Source Code for Module jazzparser.formalisms.music_halfspan.domxml

  1  """XML processing utilites for the music_halfspan formalism. 
  2   
  3  These include building internal representations from the XML grammar  
  4  definitions. 
  5   
  6  """ 
  7  """ 
  8  ============================== License ======================================== 
  9   Copyright (C) 2008, 2010-12 University of Edinburgh, Mark Granroth-Wilding 
 10    
 11   This file is part of The Jazz Parser. 
 12    
 13   The Jazz Parser is free software: you can redistribute it and/or modify 
 14   it under the terms of the GNU General Public License as published by 
 15   the Free Software Foundation, either version 3 of the License, or 
 16   (at your option) any later version. 
 17    
 18   The Jazz Parser is distributed in the hope that it will be useful, 
 19   but WITHOUT ANY WARRANTY; without even the implied warranty of 
 20   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the 
 21   GNU General Public License for more details. 
 22    
 23   You should have received a copy of the GNU General Public License 
 24   along with The Jazz Parser.  If not, see <http://www.gnu.org/licenses/>. 
 25   
 26  ============================ End license ====================================== 
 27   
 28  """ 
 29  __author__ = "Mark Granroth-Wilding <mark.granroth-wilding@ed.ac.uk>"  
 30   
 31  import re, logging 
 32  from jazzparser.utils.chords import ChordError 
 33  from jazzparser.grammar import GrammarReadError 
 34  from .syntax import AtomicCategory, ComplexCategory, Slash, Sign, HalfCategory 
 35  from .semantics import Semantics, DummyLogicalForm, Leftonto, Variable, \ 
 36              LexicalCoordinate, multi_apply, multi_abstract, Rightonto, Now, \ 
 37              List 
 38  from jazzparser.utils.domxml import remove_unwanted_elements, \ 
 39                              require_attrs, require_attr 
 40   
 41  # Get the logger from the logging system 
 42  logger = logging.getLogger("main_logger") 
 43   
 44   
45 -def build_sign_from_node(element):
46 """ 47 Given the XML element of a CCG sign, builds the internal 48 representation. 49 50 """ 51 # Assume there's only one child we're interested in (the category) 52 child_elements = remove_unwanted_elements(element.childNodes) 53 cat_elem = child_elements[0] 54 # Get the category structure from the category node 55 gramcategory = build_category_from_node(cat_elem) 56 57 lf_elems = element.getElementsByTagName("lf") 58 if len(lf_elems) == 0 or len(remove_unwanted_elements(lf_elems[0].childNodes)) == 0: 59 raise GrammarReadError, "No logical form found for entry: "\ 60 "%s. What is syntax without semantics?" % cat_elem.toxml() 61 # Steedman, 2010 (private correspondence) 62 lf_elem = lf_elems[0] 63 lf_children = remove_unwanted_elements(lf_elem.childNodes) 64 ## Get semantics from the lf node 65 sems = build_lf_from_node(lf_children[0]) 66 if sems is None: 67 raise GrammarReadError, "Could not build semantic " \ 68 "representation for %s." % lf_elem.toxml() 69 sems = Semantics(sems) 70 71 # Store the full category for this entry 72 return Sign(gramcategory, sems)
73
74 -def build_category_from_node(element):
75 """ 76 Uses an XML DOM node that represents a CCG category (i.e. a structure 77 of atomcats and complexcats) to build a CCG category in our internal 78 representation. 79 80 @return: a Category subclass that is the root of the category structure 81 82 """ 83 halfcat_re = re.compile(r'^(?P<root>.+?)(\[(?P<functions>(T|D|S)(\|(T|D|S))*)\])?$') 84 def _load_halfcat(name): 85 """Try building a HalfCategory from this string""" 86 match = halfcat_re.match(name) 87 if match is None: 88 raise GrammarReadError, "%s is not a valid half-category. Reading element %s." % (name, element.toxml()) 89 root = match.groupdict()['root'] 90 functions = match.groupdict()['functions'] 91 if functions is None: 92 # Default to tonic 93 functions = ["T"] 94 else: 95 functions = functions.split("|") 96 return HalfCategory(root, functions)
97 98 def _required_attr(el, name): 99 attr_el = element.attributes.getNamedItem(name) 100 if attr_el is None: 101 raise GrammarReadError, "required attribute '%s': %s" % (name, el.toxml()) 102 return attr_el.value 103 104 if (element.nodeName== "atomcat"): 105 # Base case: atomic category 106 107 # Read the from attribute 108 root_name = _required_attr(element, "root") 109 half_root = _load_halfcat(root_name) 110 111 # All lexical tonic categories have the same start and end 112 return AtomicCategory(half_root, half_root.copy()) 113 elif (element.nodeName == "complexcat"): 114 # Complex category 115 116 # Read the argument and result attributes 117 arg_name = _required_attr(element, "arg") 118 result_name = _required_attr(element,"res") 119 direction = _required_attr(element,"dir") 120 if (direction == "/"): 121 forward = True 122 elif (direction == "\\"): 123 forward = False 124 else: 125 raise GrammarReadError, "%s is not a valid slash direction: should be / or \\" % direction 126 127 mode_el = element.attributes.getNamedItem("modality") 128 # Default to modality=None if one hasn't been set and let Slash default 129 if mode_el is not None: 130 modality = mode_el.value 131 else: 132 modality = None 133 slash = Slash(forward, modality=modality) 134 135 result = _load_halfcat(result_name) 136 argument = _load_halfcat(arg_name) 137 138 category = ComplexCategory(result, slash, argument) 139 return category 140 else: 141 # Not recognised as an atomcat or complexcat 142 raise GrammarReadError, "Unrecognised category node %s" % element 143
144 -def build_lf_from_node(elem):
145 """ 146 Given the "lf" node of a lexical entry, builds a logical form 147 representing it internally. 148 149 @return: a LogicalForm built from the node 150 151 """ 152 name = elem.nodeName 153 if name == "point": 154 # A point in the (as yet equally tempered) tonal space, 155 # relative to the chord of the chord 156 x,y = require_attrs(elem, ["x", "y"]) 157 x,y = int(x), int(y) 158 159 if not 0 <= x < 4 or not 0 <= y < 3: 160 raise GrammarReadError, "equal temperament tonal space "\ 161 "points should be between (0,0) and (3,2): got (%d,%d)" \ 162 % (x,y) 163 # Shouldn't be any children 164 subnodes = remove_unwanted_elements(elem.childNodes) 165 if len(subnodes) != 0: 166 raise GrammarReadError, "A tonal space point cannot have children." 167 168 return LexicalCoordinate((x,y)) 169 elif name == "list": 170 # A path of points (usually just one point) 171 subnodes = remove_unwanted_elements(elem.childNodes) 172 173 children = [build_lf_from_node(node) for node in subnodes] 174 return List(children) 175 elif name == "leftonto": 176 # A leftonto predicate literal 177 178 # Shouldn't be any children 179 subnodes = remove_unwanted_elements(elem.childNodes) 180 if len(subnodes) != 0: 181 raise GrammarReadError, "A leftonto predicate cannot "\ 182 "have children." 183 184 return Leftonto() 185 elif name == "rightonto": 186 # A rightonto predicate literal 187 188 # Shouldn't be any children 189 subnodes = remove_unwanted_elements(elem.childNodes) 190 if len(subnodes) != 0: 191 raise GrammarReadError, "A rightonto predicate cannot "\ 192 "have children." 193 194 return Rightonto() 195 elif name == "now": 196 # A now predicate literal 197 198 # Shouldn't be any children 199 subnodes = remove_unwanted_elements(elem.childNodes) 200 if len(subnodes) != 0: 201 raise GrammarReadError, "A now predicate cannot "\ 202 "have children." 203 204 return Now() 205 elif name == "abstraction": 206 # Lambda abstraction 207 # All children except the last are abstracted variables 208 subnodes = remove_unwanted_elements(elem.childNodes) 209 if len(subnodes) < 2: 210 raise GrammarReadError, "No subexpression in lambda "\ 211 "abstraction: %s" % elem.toxml() 212 variables = [build_lf_from_node(node) for node in subnodes[:-1]] 213 for var in variables: 214 if not isinstance(var, Variable): 215 raise GrammarReadError, "Can only abstract over "\ 216 "variables, not %s" % type(var).__name__ 217 expression = build_lf_from_node(subnodes[-1]) 218 219 return multi_abstract(*tuple(variables+[expression])) 220 elif name == "application": 221 # Function application 222 # Recursively build functor and argument LFs 223 subnodes = remove_unwanted_elements(elem.childNodes) 224 if len(subnodes) < 2: 225 raise GrammarReadError, "Function application needs to "\ 226 "have at least two subnodes" 227 children = [build_lf_from_node(node) for node in subnodes] 228 229 return multi_apply(*children) 230 elif name == "variable": 231 # Variable reference 232 varid = require_attr(elem, "name") 233 234 return Variable(varid) 235 else: 236 raise GrammarReadError, "Got invalid node %s in LF" % name
237