def accent_by_position(self, verse: str) -> str: """:param verse: a line of unaccented hexameter verse :return: the same line with vowels accented by position >>> print(HexameterScanner().accent_by_position( ... "Arma virumque cano, Troiae qui primus ab oris").lstrip()) Ārma virūmque canō Trojae quī primus ab oris """ line = verse.translate(self.punctuation_substitutions) line = self.transform_i_to_j(line) marks = list(line) # Vowels followed by 2 consonants # The digraphs ch, ph, th, qu and sometimes gu and su count as single consonants. # see http://people.virginia.edu/~jdk3t/epicintrog/scansion.htm marks = StringUtils.overwrite( marks, "[{}][{}][{}]".format(self.constants.VOWELS, self.constants.CONSONANTS, self.constants.CONSONANTS_WO_H), self.constants.STRESSED) # one space (or more for 'dropped' punctuation may intervene) marks = StringUtils.overwrite( marks, r"[{}][{}]\s*[{}]".format(self.constants.VOWELS, self.constants.CONSONANTS, self.constants.CONSONANTS_WO_H), self.constants.STRESSED) # ... if both consonants are in the next word, the vowel may be long # .... but it could be short if the vowel is not on the thesis/emphatic part of the foot # ... see Gildersleeve and Lodge p.446 marks = StringUtils.overwrite( marks, r"[{}]\s*[{}][{}]".format(self.constants.VOWELS, self.constants.CONSONANTS, self.constants.CONSONANTS_WO_H), self.constants.STRESSED) # x is considered as two letters marks = StringUtils.overwrite(marks, "[{}][xX]".format(self.constants.VOWELS), self.constants.STRESSED) # z is considered as two letters marks = StringUtils.overwrite( marks, r"[{}][zZ]".format(self.constants.VOWELS), self.constants.STRESSED) original_verse = list(line) for idx, word in enumerate(original_verse): if marks[idx] == self.constants.STRESSED: original_verse[idx] = self.constants.VOWELS_TO_ACCENTS[ original_verse[idx]] return "".join(original_verse)
def transform_i_to_j_optional(self, line: str) -> str: """Sometimes for the demands of meter a more permissive i to j transformation is warranted. :param line: :return: >>> print(HexameterScanner().transform_i_to_j_optional("Italiam")) Italjam >>> print(HexameterScanner().transform_i_to_j_optional("Lāvīniaque")) Lāvīnjaque >>> print(HexameterScanner().transform_i_to_j_optional("omnium")) omnjum """ words = line.split(" ") space_list = StringUtils.space_list(line) corrected_words = [] for word in words: found = False for prefix in self.constants.PREFIXES: if word.startswith(prefix) and word != prefix: corrected_words.append( self.syllabifier.convert_consonantal_i(prefix)) corrected_words.append( self.syllabifier.convert_consonantal_i( word[len(prefix):])) found = True break if not found: corrected_words.append( self.syllabifier.convert_consonantal_i(word)) new_line = StringUtils.join_syllables_spaces(corrected_words, space_list) # the following two may be tunable and subject to improvement char_list = StringUtils.overwrite( list(new_line), "[bcdfgjkmpqrstvwxzBCDFGHJKMPQRSTVWXZ][i][{}]".format( self.constants.VOWELS_WO_I), "j", 1) char_list = StringUtils.overwrite( char_list, "[{}][iI][{}]".format(self.constants.LIQUIDS, self.constants.VOWELS_WO_I), "j", 1) return "".join(char_list)
def transform_i_to_j(self, line: str) -> str: """Transform instances of consonantal i to j :param line: :return: >>> print(HexameterScanner().transform_i_to_j("iactātus")) jactātus >>> print(HexameterScanner().transform_i_to_j("bracchia")) bracchia """ words = line.split(" ") space_list = StringUtils.space_list(line) corrected_words = [] for word in words: found = False for prefix in self.constants.PREFIXES: if word.startswith(prefix) and word != prefix: corrected_words.append( self.syllabifier.convert_consonantal_i(prefix)) corrected_words.append( self.syllabifier.convert_consonantal_i( word[len(prefix):])) found = True break if not found: corrected_words.append( self.syllabifier.convert_consonantal_i(word)) new_line = StringUtils.join_syllables_spaces(corrected_words, space_list) char_list = StringUtils.overwrite( list(new_line), r"\b[iī][{}]".format(self.constants.VOWELS + self.constants.ACCENTED_VOWELS), "j") char_list = StringUtils.overwrite( char_list, r"\b[I][{}]".format(self.constants.VOWELS_WO_I), "J") char_list = StringUtils.overwrite( char_list, r"[{}][i][{}]".format(self.constants.VOWELS_WO_I, self.constants.VOWELS), "j", 1) return "".join(char_list)
def transform_i_to_j_optional(self, line: str) -> str: """Sometimes for the demands of meter a more permissive i to j transformation is warranted. :param line: :return: >>> print(VerseScanner().transform_i_to_j_optional("Italiam")) Italjam >>> print(VerseScanner().transform_i_to_j_optional("Lāvīniaque")) Lāvīnjaque >>> print(VerseScanner().transform_i_to_j_optional("omnium")) omnjum """ words = line.split(" ") space_list = StringUtils.space_list(line) corrected_words = [] for word in words: found = False for prefix in self.constants.PREFIXES: if word.startswith(prefix) and word != prefix: corrected_words.append(self.syllabifier.convert_consonantal_i(prefix)) corrected_words.append( self.syllabifier.convert_consonantal_i(word[len(prefix):])) found = True break if not found: corrected_words.append(self.syllabifier.convert_consonantal_i(word)) new_line = StringUtils.join_syllables_spaces(corrected_words, space_list) # the following two may be tunable and subject to improvement char_list = StringUtils.overwrite(list(new_line), "[bcdfgjkmpqrstvwxzBCDFGHJKMPQRSTVWXZ][i][{}]".format( self.constants.VOWELS_WO_I), "j", 1) char_list = StringUtils.overwrite(char_list, "[{}][iI][{}]".format(self.constants.LIQUIDS, self.constants.VOWELS_WO_I), "j", 1) return "".join(char_list)
def transform_i_to_j(self, line: str) -> str: """Transform instances of consonantal i to j :param line: :return: >>> print(VerseScanner().transform_i_to_j("iactātus")) jactātus >>> print(VerseScanner().transform_i_to_j("bracchia")) bracchia """ words = line.split(" ") space_list = StringUtils.space_list(line) corrected_words = [] for word in words: found = False for prefix in self.constants.PREFIXES: if word.startswith(prefix) and word != prefix: corrected_words.append(self.syllabifier.convert_consonantal_i(prefix)) corrected_words.append( self.syllabifier.convert_consonantal_i(word[len(prefix):])) found = True break if not found: corrected_words.append(self.syllabifier.convert_consonantal_i(word)) new_line = StringUtils.join_syllables_spaces(corrected_words, space_list) char_list = StringUtils.overwrite(list(new_line), r"\b[iī][{}]".format( self.constants.VOWELS + self.constants.ACCENTED_VOWELS), "j") char_list = StringUtils.overwrite(char_list, r"\b[I][{}]".format(self.constants.VOWELS_WO_I), "J") char_list = StringUtils.overwrite(char_list, r"[{}][i][{}]".format( self.constants.VOWELS_WO_I, self.constants.VOWELS), "j", 1) return "".join(char_list)
def accent_by_position(self, verse_line: str) -> str: """Accent vowels according to the rules of scansion. :param verse: a line of unaccented verse :return: the same line with vowels accented by position >>> print(VerseScanner().accent_by_position( ... "Arma virumque cano, Troiae qui primus ab oris").lstrip()) Ārma virūmque canō Trojae qui primus ab oris """ line = verse_line.translate(self.punctuation_substitutions) line = self.transform_i_to_j(line) marks = list(line) # locate and save dipthong positions since we don't want them being accented dipthong_positions = [] for dipth in self.constants.DIPTHONGS: if dipth in line: dipthong_positions.append(line.find(dipth)) # Vowels followed by 2 consonants # The digraphs ch, ph, th, qu and sometimes gu and su count as single consonants. # see http://people.virginia.edu/~jdk3t/epicintrog/scansion.htm marks = StringUtils.overwrite(marks, "[{}][{}][{}]".format( self.constants.VOWELS, self.constants.CONSONANTS, self.constants.CONSONANTS_WO_H), self.constants.STRESSED) # one space (or more for 'dropped' punctuation may intervene) marks = StringUtils.overwrite(marks, r"[{}][{}]\s*[{}]".format( self.constants.VOWELS, self.constants.CONSONANTS, self.constants.CONSONANTS_WO_H), self.constants.STRESSED) # ... if both consonants are in the next word, the vowel may be long # .... but it could be short if the vowel is not on the thesis/emphatic part of the foot # ... see Gildersleeve and Lodge p.446 marks = StringUtils.overwrite(marks, r"[{}]\s*[{}][{}]".format( self.constants.VOWELS, self.constants.CONSONANTS, self.constants.CONSONANTS_WO_H), self.constants.STRESSED) # x is considered as two letters marks = StringUtils.overwrite(marks, "[{}][xX]".format(self.constants.VOWELS), self.constants.STRESSED) # z is considered as two letters marks = StringUtils.overwrite(marks, r"[{}][zZ]".format(self.constants.VOWELS), self.constants.STRESSED) original_verse = list(line) for idx, word in enumerate(original_verse): if marks[idx] == self.constants.STRESSED: original_verse[idx] = self.constants.VOWELS_TO_ACCENTS[original_verse[idx]] # make sure dipthongs aren't accented for idx in dipthong_positions: if original_verse[idx + 1] in self.constants.ACCENTS_TO_VOWELS: original_verse[idx + 1] = self.constants.ACCENTS_TO_VOWELS[original_verse[idx + 1]] return "".join(original_verse)