1 package Text::Tradition::Language::Latin;
6 use Text::Tradition::Language::Base qw/ lemmatize_treetagger treetagger_struct
12 Text::Tradition::Language::Latin - language-specific module for Latin
16 Implements morphology lookup for French words in context. This module
17 depends on the Morph::Perseus module for access to PhiloLogic database data.
18 It also depends on the TreeTagger software
19 (L<http://www.ims.uni-stuttgart.de/projekte/corplex/TreeTagger/>), which is
20 (for now) expected to be installed in $MORPHDIR/TreeTagger.
24 =head2 lemmatize( $text )
26 Evaluates the string using the Flemm package, and returns the results.
31 use_ok( 'Text::Tradition::Language::Latin' );
33 eval "use Morph::Perseus";
37 skip "Package Morph::Perseus not found" if $err;
39 my $trad = Text::Tradition->new(
40 'language' => 'Latin',
41 'file' => 't/data/legendfrag.xml',
45 foreach my $r ( $trad->collation->readings ) {
47 ok( $r->has_lexemes, "Reading $r has one or more lexemes" );
48 my @lex = $r->lexemes;
49 my $lexstr = join( '', map { $_->string } @lex );
50 my $textstr = $r->text;
52 is( $textstr, $lexstr, "Lexemes for reading $r match the reading" );
53 foreach my $l ( @lex ) {
54 next unless $l->matches;
55 next if $l->is_disambiguated;
56 printf( "Ambiguous lexeme %s for reading %s:\n\t%s\n", $l->string, $r->id,
57 join( "\n\t", map { $_->lemma . ': ' . $_->morphology->to_string } $l->matching_forms ) );
61 is( $ambig, 7, "Found 7 ambiguous forms as expected" );
69 my $tradition = shift;
71 'language' => 'Latin',
72 'callback' => sub { _perseus_lookup_tt( @_ ) }
74 return lemmatize_treetagger( $tradition, %opts );
77 =head2 reading_lookup( $rdg[, $rdg, ...] )
79 Looks up one or more readings using the Perseus package, and returns the
80 possible results. This skips the tree tagger / tokenizer, returning any
81 match for the word string(s) in the morphology DB.
87 return map { _perseus_lookup_str( $_ ) } @words;
90 =head2 morphology_tags
92 Return a data structure describing the available parts of speech and their attributes.
98 load 'Morph::Perseus::Structure';
100 warn "Not using Perseus Latin tags";
102 return lfs_morph_tags();
112 load 'Morph::Perseus';
113 load 'Morph::Perseus::Structure';
114 $morph = Morph::Perseus->connect( 'Latin' );
116 warn "Cannot do Latin word lemmatization without Morph::Perseus: @_";
123 # passive verbs (-or)
124 # T sapientia -> sapientia
126 # T occulta -> occultus (with occulo in next field, hmm...)
133 'aperte' => 'apertus',
134 'evolvo' => 'exvolvo',
135 'inquiam' => 'inquam',
136 'intelligo' => 'intellego',
140 'male' => 'malus|malum',
141 'multum' => 'multus',
144 'occultum' => 'occultus',
145 'peregrinans' => 'peregrinor',
146 'perfectus' => 'perficio',
148 'praesente' => 'praesens',
150 'quotidianus' => 'cottidianus',
152 'septem' => 'septimus',
153 'Spiritum' => 'spiritus',
154 'viriliter' => 'virilis', # TODO special case -iter?
157 'datum' => 'do|data|datus',
158 'forte' => 'fors|fortis',
159 'vere' => 'verum|verus',
162 sub _perseus_lookup_tt {
163 my( $orig, $pos, $lemma ) = split( /\t/, $_[0] );
165 return unlesss $morph;
166 my $result = $morph->lookup( $orig );
167 # Discard results that don't match the lemma, unless lemma is unknown
168 my @orig = @{$result->{'objects'}};
170 unless( $lemma eq '<unknown>' || $lemma =~ /^\W+$/ ) {
171 # TODO Perseus lemma might have a number on the end, yuck.
172 # multiple lemmata separated with |
173 $lemma =~ s/[^\w|]//g;
174 $lemma = $excep{$lemma} if exists $excep{$lemma};
178 map { $lems{$_} = 1; $lems{lc($_)} = 1 } split( /\|/, $lemma );
179 # Now match the lemmata from Treetagger to the lemmata and alt_ls
184 my $y = $_->alt_lex || '';
186 $lems{$x} || $lems{$y};
188 warn "TreeTagger lemma $lemma matched no results from Perseus for $orig"
191 @ret = @orig unless @ret;
193 my %unique_wordforms;
194 foreach my $obj ( @ret ) {
195 my $wf = _wordform_from_row( $obj );
196 $unique_wordforms{$wf->to_string} = $wf;
198 ## TODO Use TreeTagger info - requires serious hacking of Lingua::TagSet
199 # Discard results that don't match the given TreeTagger POS, unless
200 # that leaves zero results
201 # my $ttstruct = treetagger_struct( $pos );
202 # my @ttmatch = grep { $ttstruct->is_compatible( $_->morphology ) } @wordforms;
203 # unless( @ttmatch ) {
204 # warn "TreeTagger POS $pos matched no results from Perseus for $orig";
205 # @ttmatch = @wordforms;
208 return values( %unique_wordforms );
211 sub _perseus_lookup_str {
214 return unless $morph;
215 # Simple morph DB lookup, and return the results.
216 my $result = $morph->lookup( $orig );
217 return map { _wordform_from_row( $_ ) } @{$result->{'objects'}};
222 sub _wordform_from_row {
226 # M::P::St will be loaded already if we got here
227 $mpstruct = Morph::Perseus::Structure->from_tag( $rowobj->code );
229 warn "Could not create morphology structure from "
230 . $rowobj->code . ": $!";
232 my $lemma = $rowobj->lemma;
233 $lemma =~ s/^(\D+)\d*$/$1/;
234 my $wf = Text::Tradition::Collation::Reading::WordForm->new(
235 'language' => 'Latin',
237 'morphology' => $mpstruct,