1 """Data structures for the Jazz Parser.
2
3 Basic data types for the Jazz Parser.
4
5 """
6 """
7 ============================== License ========================================
8 Copyright (C) 2008, 2010-12 University of Edinburgh, Mark Granroth-Wilding
9
10 This file is part of The Jazz Parser.
11
12 The Jazz Parser is free software: you can redistribute it and/or modify
13 it under the terms of the GNU General Public License as published by
14 the Free Software Foundation, either version 3 of the License, or
15 (at your option) any later version.
16
17 The Jazz Parser is distributed in the hope that it will be useful,
18 but WITHOUT ANY WARRANTY; without even the implied warranty of
19 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 GNU General Public License for more details.
21
22 You should have received a copy of the GNU General Public License
23 along with The Jazz Parser. If not, see <http://www.gnu.org/licenses/>.
24
25 ============================ End license ======================================
26
27 """
28 __author__ = "Mark Granroth-Wilding <mark.granroth-wilding@ed.ac.uk>"
29
30 import copy, re
31 import logging
32
33 from jazzparser.utils.base import filter_latex
34 from jazzparser.utils.chords import int_to_chord_numeral, chord_numeral_to_int, \
35 int_to_ly_note, ChordError, int_to_pitch_class, \
36 pitch_class_to_int
37 from jazzparser.data.db_mirrors import Chord as MirrorChord
38
39
40 logger = logging.getLogger("main_logger")
41
42 ROMAN_REGEX = re.compile(r"""
43 ^ # Start at the very beginning
44 (?P<root>
45 (?P<accidental>b|\#)? # Flat or sharp, or neither
46 (?P<numeral>I{1,3}|I?V|VI{0,2}|X|Y|Z) # Chord numeral: I-III or IV-V or V-VII; or variable: X, Y, Z
47 )
48 (?P<type>m(7|,b5|,M7)?|aug(7|,M7)?
49 |o7|%7|sus4(,7)?|b5(,7|,M7)?
50 |M7|7|\#5,m7)? # All the chord types (triad+1) we allow
51 (\((?P<additions>6|9|b9|b10
52 |13|\+9|\+11)\))? # Extra additions must come after the chord type in brackets
53 $ # Match the whole string
54 """, re.VERBOSE)
55
56 PITCH_REGEX = re.compile(r"""
57 ^
58 (?P<root>
59 (?P<pitch>[A-G]) # Chord numeral: I-III or IV-V or V-VII; or variable: X, Y, Z
60 (?P<accidental>b|\#)? # Flat or sharp, or neither
61 )
62 (?P<type>m(7|,b5|,M7)?|aug(7|,M7)?
63 |o7|%7|sus4(,7)?|b5(,7|,M7)?
64 |M7|7|\#5,m7)? # All the chord types (triad+1) we allow
65 (\((?P<additions>6|9|b9|b10
66 |13|\+9|\+11)\))? # Extra additions must come after the chord type in brackets
67 $
68 """, re.VERBOSE)
69
70
71 -class Chord(object):
72 """
73 A Chord object represents a single chord in an input
74 sequence to the parser.
75
76 This was the original way of processing input to the parser and still
77 lurks around. However, a better representation is provided by
78 L{jazzparser.data.db_mirrors.Chord}, which is specifically designed
79 to replicate the data structure in the corpus database.
80 Instances of this class can be converted to a db-mirror chord using
81 L{to_db_mirror}. This class is now used mainly for processing textual input.
82
83 """
84
85 TYPE_SYMBOLS = {
86 1 : "",
87 2 : "m",
88 3 : "M7",
89 4 : "o7",
90 5 : "%7",
91 6 : "aug",
92 7 : "m,b5",
93 8 : "b5",
94 9 : "m,M7",
95 10 : "7",
96 11 : "m7",
97 12 : "aug7",
98 13 : "b5,7",
99 14 : "sus4",
100 15 : "sus4,7",
101 16 : "aug,M7",
102 17 : "b5,M7",
103 18 : "#5,m7",
104 }
105
106 SEVEN_TYPES = [ 4, 5, 6, 7, 8, 10, 11, 12, 13, 14, 15, 18 ]
107
108 SEVEN_IMPLIED_ADDITIONS = [ "9", "b9", "b10", "13", "+9", "+11" ]
109
110
111 IMPLIED_SEVEN_MAPPINGS = {
112 1 : 10,
113 2 : 11,
114 6 : 12,
115 7 : 5,
116 8 : 13,
117 14: 15
118 }
119
126
137
149
150
151 root = property(_get_root, _set_root)
152 root_numeral = property(_get_root_numeral, _set_root_numeral)
153 type = property(_get_type, _set_type)
154
155 - def __init__(self, root_numeral="C", type="", additions=None, roman=False):
162
164 if self.root is None:
165 return 13
166 return self.root
167
169 return not (self == other)
170
174
179
192 tetrad_type = property(_get_tetrad_type)
193
202
205
208
209 @staticmethod
211 """ Returns the interval from chord1 to chord2. """
212 return (chord2.root - chord1.root) % 12
213
214 @staticmethod
215 - def from_name(chord_name, roman=False, permissive=False):
216 """
217 Given a string chord name, returns the Chord object corresponding
218 to that name.
219
220 C{permissive} allows the label to be interpreted as a roman numeral
221 or a pitch label.
222
223 """
224
225 if permissive:
226
227 parse = ROMAN_REGEX.match(chord_name.strip())
228 if parse is None:
229 parse = PITCH_REGEX.match(chord_name.strip())
230 roman = False
231 else:
232 roman = True
233 elif roman:
234 parse = ROMAN_REGEX.match(chord_name.strip())
235 else:
236 parse = PITCH_REGEX.match(chord_name.strip())
237
238 if parse is None:
239 raise ChordError, "invalid chord symbol (%s mode): %s" % \
240 (permissive and "either" or (roman and "roman" or "pitch"),
241 chord_name)
242 result = parse.groupdict()
243 root = result['root']
244 chord_type_name = result['type']
245 additions = result['additions']
246
247
248 return Chord(root_numeral=root, type=chord_type_name,
249 additions=additions, roman=roman)
250
252 """
253 Produce a db mirror Chord instance (see L{jazzparser.data.db_mirrors}
254 that represents the same chord as this.
255
256 """
257 mirror = MirrorChord(root=self.root,
258 type=self.type,
259 additions=self.additions)
260 return mirror
261
264 """
265 Stores a trace of the derivation of a particular CCGCategory node
266 and is associated with that category. For parse results, these structures
267 will often be so large that there's no hope of being able to print the
268 thing (even recursing to count the size can be prohibitively slow!).
269
270 """
271 - def __init__(self, result, rule=None, args=[], word=None):
272 """
273 rule and args may be specified to give the derivation node an
274 initial pointer to a rule that was applied and a list of the
275 arguments for that rule.
276 Add more rules using add_rule().
277
278 All rule applications stored should have resulted in result.
279 """
280
281 self.rules = []
282 self.result = result
283 self.word = word
284 if rule is not None:
285 self.rules.append((rule,args))
286
288 """
289 Add a rule application to the derivation node (that resulted in the
290 same category as other rule applications stored here).
291 The rule is a pointer to the rule object that was applied.
292 The args is a list of the arguments to which the rule was applied.
293 """
294 self.rules.append((rule,args))
295
298
301
303 output = ""
304 output += indent + signfmt(self.result) + "\n"
305 if self.word is not None:
306 output += "%s | \"%s\"\n" % (indent, self.word)
307 else:
308 for rule in self.rules:
309 (ruleobj, arglist) = rule
310 output += indent + " | from %s (%s) applied to\n" % \
311 (ruleobj.readable_rule, ruleobj.name)
312 for arg in arglist:
313 output += arg.str_indent(indent+" | ", signfmt=signfmt)
314
315 return output
316
318 if self.word is not None:
319 return 1
320 else:
321 size = 0
322 for rule in self.rules:
323 size += 1
324 for arg in rule[1]:
325 size += arg.get_size()
326 return size
327
330 """
331 Stores a rational fraction as a numerator and denominator.
332 Fractions can be output as strings and also read in from strings
333 (use Fraction(string=<string>), or just Fraction(<string>)).
334
335 The format used is "i", where i
336 is an integer if the fraction is an exact integer, or "i n/d", where
337 i is the integer part, n the numerator and d the denominator.
338
339 """
340 - def __init__(self, numerator=0, denominator=1, string=None):
341 self._denominator = 1
342
343 self.numerator = numerator
344 self.denominator = denominator
345 if isinstance(numerator, str):
346 string = numerator
347 if string is not None:
348 whole_string = string
349
350 string = string.strip()
351
352
353 if string.startswith("-"):
354 neg = True
355 string = string.lstrip("-").strip()
356 else:
357 neg = False
358
359 string_parts = string.split(" ")
360 try:
361 if len(string_parts) == 1:
362
363 fract_parts = [p.strip() for p in string.split("/")]
364 if len(fract_parts) == 1:
365
366 self.numerator = int(string)
367 self.denominator = 1
368 elif len(fract_parts) == 2:
369
370 self.numerator = int(fract_parts[0])
371 self.denominator = int(fract_parts[1])
372 else:
373 raise Fraction.ValueError, "Too many slashes in "\
374 "fraction '%s'." % whole_string
375 else:
376 integer = int(string_parts[0])
377 fract_parts = string_parts[1].split("/")
378 if len(fract_parts) == 2:
379 numerator = int(fract_parts[0])
380 denominator = int(fract_parts[1])
381 else:
382 raise Fraction.ValueError, "Too many slashes in "\
383 "fraction '%s'." % whole_string
384 self.numerator = numerator + integer * denominator
385 self.denominator = denominator
386 except ValueError:
387 raise Fraction.ValueError, "Error parsing fraction "\
388 "string '%s'." % string
389 if neg:
390
391 self.numerator = -self.numerator
392
394 return self._denominator
396 if val == 0:
397 raise ZeroDivisionError, "tried to set a Fraction's denominator "\
398 "to zero"
399 self._denominator = val
400 denominator = property(_get_denominator, _set_denominator)
401
408
424
429
439
440 __repr__ = __str__
441
449
450
451
465
466
467 plus = __add__
468
470
471 return self + other
472
475
477 return self + (-other)
478
480 return (-self) + other
481
493
495
496 return self * other
497
509
512
515
518
521
522
523
525 if other is None:
526 return 1
527
528 if type(other) == int:
529 other = Fraction(other)
530
531 if type(other) == float:
532 return cmp(float(self), other)
533
534 if other.__class__ != self.__class__:
535 raise TypeError, "Fraction.__cmp__(self,other) requires other to "\
536 "be of type Fraction or int. Type was %s." % other.__class__
537
538 selfnum = self.numerator
539 othernum = other.numerator
540 selfnum *= other.denominator
541 othernum *= self.denominator
542 if selfnum == othernum:
543 return 0
544 elif selfnum > othernum:
545 return 1
546 else:
547 return -1
548
551
554
556 while num2 != 0:
557 remainder = num1 % num2
558 num1 = num2
559 num2 = remainder
560 return num1
561
564 """
565 A simple implementation of a hash table using a dictionary.
566 The table is a set, since it does not store duplicate entries.
567
568 Stores pointers both in a hash table (dictionary) and a list,
569 so that the values can be retreived quickly.
570
571 """
573 self.table = {}
574 self.list = []
575
577 """
578 When a value already exists in the table, this is called
579 instead of adding the value. By default, it does nothing
580 (i.e. drops the new value), but you can override it if you
581 want to do something else to combine the values.
582
583 Note that your custom methods must only modify the sign
584 that's already in the set (C{existing_value}), not add a
585 new sign and not modify C{new_value}.
586
587 """
588 pass
589
591 """
592 Appends the new entry to the set if it's not already there.
593 Returns true if the entry is added, false otherwise.
594 """
595
596 key = hash(new_entry)
597 if key not in self.table:
598
599 self.table[key] = []
600 elif new_entry in self.table[key]:
601
602 index = self.table[key].index(new_entry)
603 existing_sign = self.table[key][index]
604 self._add_existing_value(existing_sign, new_entry)
605 return False
606
607 self.table[key].append(new_entry)
608
609 self.list.append(new_entry)
610 return True
611
613 """
614 Appends each of the given entries to the set.
615 Returns true if any of them is added, false otherwise.
616 """
617 added = False
618 for entry in entries:
619 app = self.append(entry)
620 if app:
621 added = True
622 return added
623
625
626 key = hash(value)
627
628 return value in self.table[key]
629
632
634 key = hash(entry)
635 if entry not in self.table[key]:
636 raise ValueError, "Tried to remove an entry that's not in the hash set."
637
638 self.table[key].remove(entry)
639
640 self.list.remove(entry)
641 return
642
645