Skip to content

Commit

Permalink
Merge pull request #826 from clemsciences/old_norse_poem_structure
Browse files Browse the repository at this point in the history
  • Loading branch information
clemsciences committed Sep 1, 2018
2 parents 5feef59 + 016096a commit 0dd1b50
Show file tree
Hide file tree
Showing 4 changed files with 169 additions and 41 deletions.
163 changes: 129 additions & 34 deletions cltk/prosody/old_norse/verse.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ def normalize(text):
return res


class VerseManager:
class MetreManager:
"""
* Fornyrðislag
* Ljóðaháttr
Expand All @@ -39,9 +39,9 @@ def is_fornyrdhislag(text: str):
>>> text1 = "Hljóðs bið ek allar\\nhelgar kindir,\\nmeiri ok minni\\nmögu Heimdallar;\\nviltu at ek, Valföðr,\\nvel fyr telja\\nforn spjöll fira,\\nþau er fremst of man."
>>> text2 = "Deyr fé,\\ndeyja frændr,\\ndeyr sjalfr it sama,\\nek veit einn,\\nat aldrei deyr:\\ndómr um dauðan hvern."
>>> VerseManager.is_fornyrdhislag(text1)
>>> MetreManager.is_fornyrdhislag(text1)
True
>>> VerseManager.is_fornyrdhislag(text2)
>>> MetreManager.is_fornyrdhislag(text2)
False
:param text:
Expand All @@ -57,9 +57,9 @@ def is_ljoodhhaattr(text: str):
>>> text1 = "Hljóðs bið ek allar\\nhelgar kindir,\\nmeiri ok minni\\nmögu Heimdallar;\\nviltu at ek, Valföðr,\\nvel fyr telja\\nforn spjöll fira,\\nþau er fremst of man."
>>> text2 = "Deyr fé,\\ndeyja frændr,\\ndeyr sjalfr it sama,\\nek veit einn,\\nat aldrei deyr:\\ndómr um dauðan hvern."
>>> VerseManager.is_ljoodhhaattr(text1)
>>> MetreManager.is_ljoodhhaattr(text1)
False
>>> VerseManager.is_ljoodhhaattr(text2)
>>> MetreManager.is_ljoodhhaattr(text2)
True
:param text:
Expand All @@ -68,6 +68,35 @@ def is_ljoodhhaattr(text: str):
lines = [line for line in text.split("\n") if line != ""]
return len(lines) == 6

@staticmethod
def load_poem_from_paragraphs(paragraphs):
"""
:param paragraphs: list of stanzas (list of strings)
:return: list of Fornyrdhislag or Ljoodhhaattr instances
"""
poem = []
for paragraph in paragraphs:
if MetreManager.is_fornyrdhislag(paragraph):
fnl = Fornyrdhislag()
fnl.from_short_lines_text(paragraph)
fnl.syllabify(old_norse_syllabifier.hierarchy)
fnl.to_phonetics()
poem.append(fnl)
elif MetreManager.is_ljoodhhaattr(paragraph):
lh = Ljoodhhaattr()
lh.from_short_lines_text(paragraph)
lh.syllabify(old_norse_syllabifier.hierarchy)
lh.to_phonetics()
poem.append(lh)
else:
stanza = UnspecifiedStanza()
stanza.from_short_lines_text(paragraph)
stanza.syllabify(old_norse_syllabifier.hierarchy)
stanza.to_phonetics()
poem.append(stanza)
return poem


class ShortLine:
def __init__(self, text):
Expand Down Expand Up @@ -148,6 +177,7 @@ class LongLine:
def __init__(self, text):
self.text = text
self.tokenized_text = tokenize_old_norse_words(text)
self.short_lines = None
self.first_sounds = []
self.syllabified = []
self.transcribed = []
Expand Down Expand Up @@ -215,7 +245,7 @@ def find_alliterations(self):
return self.alliterations, self.n_alliterations


class Verse:
class Metre:
"""
Verse, strophe or stanza. This is here a regular set of meters.
'Abstract' class which implements global methods on verse.
Expand Down Expand Up @@ -315,7 +345,89 @@ def find_alliteration(self):
return verse_alliterations, n_alliterations_lines


class Fornyrdhislag(Verse):
class UnspecifiedStanza(Metre):
"""
No specific structure. No find_alliteration because it makes only sense for long lines.
"""
def __init__(self):
Metre.__init__(self)

def from_short_lines_text(self, text: str):
"""
Example from Völsupá 28
>>> stanza = "Ein sat hon úti,\\nþá er inn aldni kom\\nyggjungr ása\\nok í augu leit.\\nHvers fregnið mik?\\nHví freistið mín?\\nAllt veit ek, Óðinn,\\nhvar þú auga falt,\\ní inum mæra\\nMímisbrunni.\\nDrekkr mjöð Mímir\\nmorgun hverjan\\naf veði Valföðrs.\\nVituð ér enn - eða hvat?"
>>> us = UnspecifiedStanza()
>>> us.from_short_lines_text(stanza)
>>> [sl.text for sl in us.short_lines]
['Ein sat hon úti,', 'þá er inn aldni kom', 'yggjungr ása', 'ok í augu leit.', 'Hvers fregnið mik?', 'Hví freistið mín?', 'Allt veit ek, Óðinn,', 'hvar þú auga falt,', 'í inum mæra', 'Mímisbrunni.', 'Drekkr mjöð Mímir', 'morgun hverjan', 'af veði Valföðrs.', 'Vituð ér enn - eða hvat?']
>>> us.long_lines
:param text:
:return:
"""
Metre.from_short_lines_text(self, text)
self.short_lines = [ShortLine(line) for line in text.split("\n") if line != ""]
self.long_lines = None

def syllabify(self, hierarchy):
"""
>>> stanza = "Ein sat hon úti,\\nþá er inn aldni kom\\nyggjungr ása\\nok í augu leit.\\nHvers fregnið mik?\\nHví freistið mín?\\nAllt veit ek, Óðinn,\\nhvar þú auga falt,\\ní inum mæra\\nMímisbrunni.\\nDrekkr mjöð Mímir\\nmorgun hverjan\\naf veði Valföðrs.\\nVituð ér enn - eða hvat?"
>>> us = UnspecifiedStanza()
>>> us.from_short_lines_text(stanza)
>>> us.syllabify(old_norse_syllabifier.hierarchy)
>>> us.syllabified_text
[[['ein'], ['sat'], ['hon'], ['út', 'i']], [['þá'], ['er'], ['inn'], ['al', 'dni'], ['kom']], [['yg', 'gjungr'], ['ás', 'a']], [['ok'], ['í'], ['aug', 'u'], ['leit']], [['hvers'], ['freg', 'nið'], ['mik']], [['hví'], ['freis', 'tið'], ['mín']], [['allt'], ['veit'], ['ek'], ['ó', 'ðinn']], [['hvar'], ['þú'], ['aug', 'a'], ['falt']], [['í'], ['i', 'num'], ['mær', 'a']], [['mí', 'mis', 'brun', 'ni']], [['drekkr'], ['mjöð'], ['mí', 'mir']], [['mor', 'gun'], ['hver', 'jan']], [['af'], ['veð', 'i'], ['val', 'föðrs']], [['vi', 'tuð'], ['ér'], ['enn'], ['eð', 'a'], ['hvat']]]
:param hierarchy:
:return:
"""
syllabifier = Syllabifier(language="old_norse", break_geminants=True)
syllabifier.set_hierarchy(hierarchy)
syllabified_text = []
for short_line in self.short_lines:
assert isinstance(short_line, ShortLine)
short_line.syllabify(syllabifier)
syllabified_text.append(short_line.syllabified)
self.syllabified_text = syllabified_text

def to_phonetics(self):
"""
>>> stanza = "Ein sat hon úti,\\nþá er inn aldni kom\\nyggjungr ása\\nok í augu leit.\\nHvers fregnið mik?\\nHví freistið mín?\\nAllt veit ek, Óðinn,\\nhvar þú auga falt,\\ní inum mæra\\nMímisbrunni.\\nDrekkr mjöð Mímir\\nmorgun hverjan\\naf veði Valföðrs.\\nVituð ér enn - eða hvat?"
>>> us = UnspecifiedStanza()
>>> us.from_short_lines_text(stanza)
>>> us.to_phonetics()
>>> us.transcribed_text
[['[ɛin]', '[sat]', '[hɔn]', '[uːti]'], ['[θaː]', '[ɛr]', '[inː]', '[aldni]', '[kɔm]'], ['[ygːjunɣr]', '[aːsa]'], ['[ɔk]', '[iː]', '[ɒuɣu]', '[lɛit]'], ['[hvɛrs]', '[frɛɣnið]', '[mik]'], ['[hviː]', '[frɛistið]', '[miːn]'], ['[alːt]', '[vɛit]', '[ɛk]', '[oːðinː]'], ['[hvar]', '[θuː]', '[ɒuɣa]', '[falt]'], ['[iː]', '[inum]', '[mɛːra]'], ['[miːmisbrunːi]'], ['[drɛkːr]', '[mjœð]', '[miːmir]'], ['[mɔrɣun]', '[hvɛrjan]'], ['[av]', '[vɛði]', '[valvœðrs]'], ['[vituð]', '[eːr]', '[ɛnː]', '[ɛða]', '[hvat]']]
:return:
"""
transcriber = Transcriber(DIPHTHONGS_IPA, DIPHTHONGS_IPA_class, IPA_class, old_norse_rules)
transcribed_text = []
phonological_features_text = []
for short_line in self.short_lines:
assert isinstance(short_line, ShortLine) or isinstance(short_line, LongLine)
short_line.to_phonetics(transcriber)
transcribed_text.append(short_line.transcribed)
phonological_features_text.append(short_line.phonological_features_text)
self.transcribed_text = transcribed_text
self.phonological_features_text = phonological_features_text

def find_alliteration(self):
"""
Alliterations in short lines make no sense.
>>> stanza = "Ein sat hon úti,\\nþá er inn aldni kom\\nyggjungr ása\\nok í augu leit.\\nHvers fregnið mik?\\nHví freistið mín?\\nAllt veit ek, Óðinn,\\nhvar þú auga falt,\\ní inum mæra\\nMímisbrunni.\\nDrekkr mjöð Mímir\\nmorgun hverjan\\naf veði Valföðrs.\\nVituð ér enn - eða hvat?"
>>> us = UnspecifiedStanza()
>>> us.from_short_lines_text(stanza)
>>> us.to_phonetics()
>>> us.find_alliteration()
([], 0)
:return:
"""
return [], 0


class Fornyrdhislag(Metre):
"""
Fornyrðislag :
Expand All @@ -336,10 +448,7 @@ class Fornyrdhislag(Verse):
"""
def __init__(self):
Verse.__init__(self)
# self.text = ""
# self.long_lines = []
# self.short_lines = []
Metre.__init__(self)

def from_short_lines_text(self, text: str):
"""
Expand Down Expand Up @@ -370,7 +479,7 @@ def syllabify(self, hierarchy):
:return:
"""
Verse.syllabify(self, hierarchy)
Metre.syllabify(self, hierarchy)

def to_phonetics(self):
"""
Expand All @@ -383,7 +492,7 @@ def to_phonetics(self):
:return:
"""
Verse.to_phonetics(self)
Metre.to_phonetics(self)

def find_alliteration(self):
"""
Expand All @@ -396,10 +505,10 @@ def find_alliteration(self):
:return:
"""
return Verse.find_alliteration(self)
return Metre.find_alliteration(self)


class Ljoodhhaattr(Verse):
class Ljoodhhaattr(Metre):
"""
Ljóðaháttr
Expand All @@ -418,11 +527,7 @@ class Ljoodhhaattr(Verse):
----------------
"""
def __init__(self):
Verse.__init__(self)
# self.text = ""
# self.long_lines = []
# self.short_lines = []
# self.syllabified_text = []
Metre.__init__(self)

def from_short_lines_text(self, text: str):
"""
Expand All @@ -438,7 +543,7 @@ def from_short_lines_text(self, text: str):
:param text:
:return:
"""
Verse.from_short_lines_text(self, text)
Metre.from_short_lines_text(self, text)
lines = [line for line in text.split("\n") if line != ""]
self.short_lines = [ShortLine(lines[0]), ShortLine(lines[1]), LongLine(lines[2]), ShortLine(lines[3]),
ShortLine(lines[4]), LongLine(lines[5])]
Expand All @@ -455,7 +560,7 @@ def syllabify(self, hierarchy):
:return:
"""
Verse.syllabify(self, hierarchy)
Metre.syllabify(self, hierarchy)

def to_phonetics(self):
"""
Expand All @@ -467,7 +572,7 @@ def to_phonetics(self):
[[['[dɐyr]', '[feː]'], ['[dɐyja]', '[frɛːndr]']], [['[dɐyr]', '[sjalvr]', '[it]', '[sama]']], [['[ɛk]', '[vɛit]', '[ɛinː]'], ['[at]', '[aldrɛi]', '[dɐyr]']], [['[doːmr]', '[um]', '[dɒuðan]', '[hvɛrn]']]]
"""
Verse.to_phonetics(self)
Metre.to_phonetics(self)

def find_alliteration(self):
"""
Expand All @@ -483,14 +588,4 @@ def find_alliteration(self):
:return:
"""
return Verse.find_alliteration(self)


if __name__ == "__main__":
poem = "Deyr fé,\ndeyja frændr,\ndeyr sjalfr it sama,\nek veit einn,\nat aldrei deyr:\ndómr um dauðan hvern."
fo = Fornyrdhislag()
fo.from_short_lines_text(poem)
fo.to_phonetics()
fo.syllabify(old_norse_syllabifier.hierarchy)
res_alliterations, res_n_alliterations_lines = fo.find_alliteration()
print("Alliterations : "+str(res_alliterations), "number : ", res_n_alliterations_lines)
return Metre.find_alliteration(self)
16 changes: 15 additions & 1 deletion cltk/tests/test_languages/test_old_norse.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
from cltk.corpus.old_norse.syllabifier import invalid_onsets
from cltk.inflection.old_norse import pronouns, nouns
import cltk.inflection.utils as decl_utils
from cltk.prosody.old_norse.verse import Fornyrdhislag, Ljoodhhaattr
from cltk.prosody.old_norse.verse import Fornyrdhislag, Ljoodhhaattr, MetreManager, UnspecifiedStanza


__author__ = ["Clément Besnier <clemsciences@aol.com>", ]
Expand Down Expand Up @@ -133,6 +133,20 @@ def test_prosody_ljoodhhaattr(self):
[[('deyr', 'deyja'), ('fé', 'frændr')], [('sjalfr', 'sjalfr')], [('einn', 'aldrei')],
[('dómr', 'um')]])

def test_poem(self):
fake_poetic_text = ["Hljóðs bið ek allar\nhelgar kindir,\nmeiri ok minni\nmögu Heimdallar;\n"
"viltu at ek, Valföðr,\nvel fyr telja\nforn spjöll fira,\nþau er fremst of man.",
"Deyr fé,\ndeyja frændr,\ndeyr sjalfr it sama,\nek veit einn,\nat aldrei deyr:\n"
"dómr um dauðan hvern.",
"Ein sat hon úti,\nþá er inn aldni kom\nyggjungr ása\nok í augu leit.\n"
"Hvers fregnið mik?\nHví freistið mín?\nAllt veit ek, Óðinn,\nhvar þú auga falt,\n"
"í inum mæra\nMímisbrunni.\nDrekkr mjöð Mímir\nmorgun hverjan\naf veði Valföðrs.\n"
"Vituð ér enn - eða hvat?"]
fake_poem = MetreManager.load_poem_from_paragraphs(fake_poetic_text)
self.assertIsInstance(fake_poem[0], Fornyrdhislag)
self.assertIsInstance(fake_poem[1], Ljoodhhaattr)
self.assertIsInstance(fake_poem[2], UnspecifiedStanza)


if __name__ == '__main__':
unittest.main()
29 changes: 24 additions & 5 deletions docs/old_norse.rst
Original file line number Diff line number Diff line change
Expand Up @@ -161,7 +161,19 @@ For a language-dependent approach, you can call the predefined sonority dictiona
Old Norse prosody
=================

* Fornyrðislag
Edda poetry is traditionally composed of the skaldic poetry and the eddic poetry.


Eddic poetry
````````````

Eddic poems designate the poems of the **Poetic Edda**. Stanza, line and verse are the three levels that characterize eddic poetry.
The poetic Edda are mainly composed of three kinds of poetic meters: *fornyrðislag*, *ljóðaháttr* and *málaháttr*.

* *Fornyrðislag*

A stanza of *fornyrðislag* has 8 short lines (or verses), 4 long-lines (or lines). Each long line has two short lines. The first verse of a line usually has an alliteration with the second verse of a line.


.. code-block:: python
Expand Down Expand Up @@ -200,10 +212,13 @@ Old Norse prosody
Out[11]: ([[('hljóðs', 'helgar')], [('meiri', 'mögu'), ('minni', 'mögu')], [], [('forn', 'fremst'), ('fira', 'fremst')]], [1, 2, 0, 2])
* Ljóðaháttr
* *Ljóðaháttr*

A stanza of *ljóðaháttr* has 6 short lines (or verses), 4 long-lines (or lines). The first and the third lines have two verses, while the second and the fourth lines have only one (longer) verse. The first verse of the first and third lines alliterates with the second verse of these lines. The second and the fourth lines contain alliterations.


.. code-block:: python
verse_alliterations, n_alliterations_lines = lj.find_alliteration()
In[1]: text2 = "Deyr fé,\ndeyja frændr,\ndeyr sjalfr it sama,\nek veit einn,\nat aldrei deyr:\ndómr um dauðan hvern."
In[2]: VerseManager.is_ljoodhhaattr(text2)
Expand Down Expand Up @@ -244,10 +259,14 @@ Old Norse prosody
Out[13]: [2, 1, 1, 1]
* *Málaháttr*

*Málaháttr* is very similar to *ljóðaháttr*, except that verses are longer. No special code has been written for this.

* Dróttkvætt
Skaldic poetry
``````````````

* Hrynhenda
*Dróttkvætt* and *hrynhenda* are examples of skaldic poetic meters.


Old Norse pronouns declension
Expand Down
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@
name='cltk',
packages=find_packages(),
url='https://github.com/cltk/cltk',
version='0.1.97',
version='0.1.98',
zip_safe=True,
test_suite='cltk.tests.test_cltk',
)

0 comments on commit 0dd1b50

Please sign in to comment.