File Coverage

blib/lib/Lingua/Romana/Perligata.pm
Criterion Covered Total %
statement 272 564 48.2
branch 157 434 36.1
condition 64 169 37.8
subroutine 35 68 51.4
pod 1 19 5.2
total 529 1254 42.1


line stmt bran cond sub pod time code
1             package Lingua::Romana::Perligata;
2              
3             our $VERSION = '0.602';
4              
5 2     2   2321 use Filter::Util::Call;
  2         1885  
  2         106  
6 2     2   983 use IO::Handle;
  2         11178  
  2         132  
7 2     2   1206 use Data::Dumper 'Dumper';
  2         12617  
  2         6236  
8              
9             my $offset = 0;
10             my $translate = 0;
11             my $debug = 0;
12              
13             sub import {
14 2     2   36 filter_add({});
15 2         68 $offset = (caller)[2]+1;
16 2         10 $translate = grep /^converte?$/i, @_[1..$#_];
17 2         8 $debug = grep /^investiga?$/i, @_[1..$#_];
18 2         6 $lex = grep /^discribe?$/i, @_[1..$#_];
19 2         105 1;
20             }
21              
22             $translator = q{
23             BEGIN {
24             $SIG{__DIE__} = sub { die Lingua::Romana::Perligata::readversum($_[0]) };
25             $SIG{__WARN__} = sub { return if $_[0] =~ /(...) interpreted as function/;
26             warn Lingua::Romana::Perligata::readversum($_[0]) };
27             }
28             };
29              
30 0     0   0 sub unimport { filter_del() }
31              
32             sub filter {
33 4     4 0 98 my($self) = @_ ;
34              
35 4         6 my $status = 1;
36 4         79 $status = filter_read(100_000);
37 4 50       20 return $status if $status<0;
38 4         11 s/\A#!.*\n//;
39 4         22 $tokens = tokenize($_);
40              
41 4         6 my @commands;
42 4         14 push @commands, conn_command($tokens,'END') while @$tokens;
43 4         10 $_ = join ";\n", map { $_->translate } @commands;
  4         12  
44              
45 4 0 33     18 if ($translate) { print "$_;\n" and exit }
  0 50       0  
    50          
46             elsif ($debug && /\S/) {
47 0         0 print "=" x 72,
48             "\nTranslated to:\n\n$_\n",
49             "=" x 72;
50             }
51 4         13 $_ = "$translator\n#line $offset\n;$_";
52 4         2652 return $status;
53             }
54              
55             #==========TOKENIZER============
56              
57 0     0 0 0 sub adversum { " ad versum " . to_rn($_[0]->{line}) . "\n" }
58 0 0   0 0 0 sub readversum { $_[0] =~ m{(.*)at\s+(\S+)\s+line\s+(\d+)(.*)}s or return $_[0];
59 0         0 return $1 . " ad $2 versum " . to_rn($3) . $4 }
60              
61             sub make_range {
62 20     20 0 34 my ($unit, $five, $ten) = @_;
63 20         43 my ($two, $three) = ($unit x 2, $unit x 3);
64 20         84 return [ "", $unit, $two, $three, $unit.$five, $five,
65             $five.$unit, $five.$two, $five.$three, $unit.$ten ];
66             }
67              
68             my @order = (
69             make_range(qw{ I V X }),
70             make_range(qw{ X L C }),
71             make_range(qw{ C D M }),
72             make_range(qw{ M I)) ((I)) }),
73             make_range(qw{ ((I)) I))) (((I))) }),
74             make_range(qw{ (((I))) I)))) ((((I)))) }),
75             make_range(qw{ ((((I)))) I))))) (((((I))))) }),
76             make_range(qw{ (((((I))))) I)))))) ((((((I)))))) }),
77             make_range(qw{ ((((((I)))))) I))))))) (((((((I))))))) }),
78             make_range(qw{ (((((((I))))))) I)))))))) ((((((((I)))))))) }),
79             );
80              
81             my %val;
82             foreach my $power (0..$#order) {
83             @val{@{$order[$power]}} = map {$_*10**$power} 0..9;
84             }
85              
86             my $roman = '(' . join(")(", map {join("|",map { quotemeta } reverse(@$_))} reverse @order) . '|)';
87              
88             sub from_rn {
89 2     2 0 5 my $val = shift;
90 2         471 @numerals = $val =~ /(?:$roman)/ix;
91 2 50       34 join("",@numerals) eq $val or return $val;
92 2         8 my $an = 0;
93 2         13 $an += $val{$_} foreach @numerals;
94 2         12 return $an;
95             }
96              
97             sub __beautify__ {
98 0     0   0 my ($text) = @_;
99 0         0 $text =~ s/\b(\d+)\b/to_rn($1)/ge;
  0         0  
100 0         0 return $text;
101             }
102              
103             sub to_rn {
104 0 0   0 0 0 return "nullum" if $_[0] == 0;
105 0         0 @digits = split '', $_[0];
106 0 0       0 return $_[0] if grep {/\D/} @digits;
  0         0  
107 0         0 my $power = 0;
108 0         0 my $rn = "";
109 0   0     0 $rn = $order[$power++][$_||0] . $rn foreach reverse @digits;
110 0         0 return $rn;
111             }
112              
113             sub getline {
114 0     0 0 0 my ($fh) = @_;
115 0 0       0 if (wantarray) {
116 0         0 my @lines = IO::Handle::getlines($fh);
117 0 0       0 s/\b($roman)\b/$1 ? from_rn($1) : $1/ge for @lines;
  0         0  
118 0         0 return @lines;
119             }
120             else {
121 0         0 my $line = IO::Handle::getline($fh);
122 0 0       0 $line =~ s/\b($roman)\b/$1 ? from_rn($1) : $1/ge;
  0         0  
123 0         0 return $line;
124             }
125             }
126              
127             sub multibless(\%$$)
128             {
129 66     66 0 108 my ($hash,$blesstype,$lextype) = @_;
130 66         256 foreach my $key (keys %$hash)
131             {
132 1514         1830 $hash->{$key}{lex} = $lextype;
133 1514         2011 bless $hash->{$key}, $blesstype;
134             }
135             }
136              
137             sub addres(\%$$)
138             {
139 16     16 0 32 my ($hash,$blesstype,$lextype) = @_;
140 16         26 my (%acc, %dat);
141 16         39 foreach my $key (keys %$hash)
142             {
143 258         328 $acc{$key.'mentum'} = { %{$hash->{$key}} };
  258         817  
144 258         346 $acc{$key.'menta'} = { %{$hash->{$key}} };
  258         924  
145 258         356 $dat{$key.'mento'} = { %{$hash->{$key}} };
  258         714  
146 258         340 $dat{$key.'mentis'} = { %{$hash->{$key}} };
  258         812  
147             }
148 16         55 multibless %acc, $blesstype, $lextype.'_ACCUSATIVE';
149 16         58 multibless %dat, $blesstype, $lextype.'_DATIVE';
150 16         545 %$hash = ( %$hash, %acc, %dat );
151             }
152              
153             sub add_genitives(\%$$$)
154             {
155 10     10 0 20 my ($hash, $blesstype, $from, $to) = @_;
156 10         41 foreach my $key (keys %$hash)
157             {
158 354         433 my $genkey = $key;
159 354 100       905 $genkey =~ s/$from$/$to/ or next;
160 76         108 $hash->{$genkey} = bless { %{$hash->{$key}},
  76         370  
161             lex => 'GENITIVE' },
162             $blesstype;
163             }
164             }
165              
166             my %literals =
167             (
168             'novumversum' => { perl => '"\n"' },
169             'biguttam' => { perl => '":"' },
170             'guttam' => { perl => '"."' },
171             'comma' => { perl => '","' },
172             'lacunam' => { perl => '" "' },
173             'stadium' => { perl => '"\t"' },
174             'parprimum' => { perl => '$1' },
175             'pardecimum' => { perl => '$10' },
176             'parsecundum' => { perl => '$2' },
177             'partertium' => { perl => '$3' },
178             'parquartum' => { perl => '$4' },
179             'parquintum' => { perl => '$5' },
180             'parsextum' => { perl => '$6' },
181             'parseptimum' => { perl => '$7' },
182             'paroctavum' => { perl => '$8' },
183             'parnonum' => { perl => '$9' },
184             'nomen' => { perl => '$<' },
185             );
186              
187             multibless %literals, 'Literal', 'ACCUSATIVE';
188             add_genitives %literals, 'Literal', 'um' => 'i';
189             add_genitives %literals, 'Literal', 'am' => 'ae';
190             add_genitives %literals, 'Literal', 'ma' => 'matis';
191              
192              
193             my %numerals =
194             (
195             'nullum' => { perl => '0' },
196             'unum' => { perl => '1' },
197             'unam' => { perl => '1' },
198             'duo' => { perl => '2' },
199             'duas' => { perl => '2' },
200             'tres' => { perl => '3' },
201             'quattuor' => { perl => '4' },
202             'quinque' => { perl => '5' },
203             'sex' => { perl => '6' },
204             'septem' => { perl => '7' },
205             'octo' => { perl => '8' },
206             'novem' => { perl => '9' },
207             'decem' => { perl => '10' },
208             );
209              
210             multibless %numerals, 'Literal', 'NUMERAL';
211              
212              
213             my %ordinals_a =
214             (
215             'nullimum' => { perl => '0' },
216             'primum' => { perl => '1' },
217             'secundum' => { perl => '2' },
218             'tertium' => { perl => '3' },
219             'quartum' => { perl => '4' },
220             'quintum' => { perl => '5' },
221             'sextum' => { perl => '6' },
222             'septimum' => { perl => '7' },
223             'octavum' => { perl => '8' },
224             'nonum' => { perl => '9' },
225             'decimum' => { perl => '10' },
226             );
227              
228             foreach ( map substr($_,0,-2), keys %ordinals_a ) {
229             $ordinals_a{"${_}os"} = $ordinals_a{"${_}um"}; # MASC. PLURALS
230             $ordinals_a{"${_}am"} = $ordinals_a{"${_}um"}; # FEMININE
231             $ordinals_a{"${_}as"} = $ordinals_a{"${_}um"}; # FEM. PLURALS
232             }
233              
234             multibless %ordinals_a, 'Ordinal', 'ORDINAL';
235             add_genitives %ordinals_a, 'Ordinal', 'um' => 'i';
236             add_genitives %ordinals_a, 'Ordinal', 'am' => 'ae';
237             # add_genitives %ordinals_a, 'Ordinal', 'os' => 'orum';
238              
239             my %ordinals_d =
240             (
241             'nullimo' => { perl => '0' },
242             'primo' => { perl => '1' },
243             'secundo' => { perl => '2' },
244             'tertio' => { perl => '3' },
245             'quarto' => { perl => '4' },
246             'quinto' => { perl => '5' },
247             'sexto' => { perl => '6' },
248             'septimo' => { perl => '7' },
249             'octavo' => { perl => '8' },
250             'nono' => { perl => '9' },
251             'decimo' => { perl => '10' },
252             );
253              
254             multibless %ordinals_d, 'Ordinal', 'ORDINAL_DATIVE';
255              
256             my %underscores =
257             (
258             'hoc' => { perl => '$_', lex => 'ACCUSATIVE'},
259             'huius' => { perl => '$_', lex => 'GENITIVE'},
260             'huic' => { perl => '$_', lex => 'DATIVE'},
261             'haec' => { perl => '@_', lex => 'ACCUSATIVE'},
262             'horum' => { perl => '@_', lex => 'GENITIVE'},
263             'his' => { perl => '@_', lex => 'DATIVE'},
264             'ianitorem' => { perl => '$/', lex => 'ACCUSATIVE' },
265             'ianitoris' => { perl => '$/', lex => 'GENITIVE' },
266             'ianitori' => { perl => '$/', lex => 'DATIVE' },
267             );
268              
269             for my $key ( keys %underscores )
270             {
271             bless $underscores{$key}, 'Literal';
272             }
273              
274             my %varmods =
275             (
276             'loco' => bless ({ perl => 'local', lex => 'OWNER_D' },
277             'ScalarMod'),
278             'locis' => bless ({ perl => 'local', lex => 'OWNER_D' },
279             'ArrMod'),
280             'meo' => bless ({ perl => 'my', lex => 'OWNER_D' },
281             'ScalarMod'),
282             'meis' => bless ({ perl => 'my', lex => 'OWNER_D' },
283             'ArrMod'),
284             'nostro' => bless ({ perl => 'our', lex => 'OWNER_D' },
285             'ScalarMod'),
286             'nostris' => bless ({ perl => 'our', lex => 'OWNER_D' },
287             'ArrMod'),
288             );
289              
290             my %streams =
291             (
292             'vestibulo' => { perl => '*STDIN' },
293             'egresso' => { perl => 'STDOUT' },
294             'oraculo' => { perl => 'STDERR' },
295             'nuntio' => { perl => '*Lingua::Romana::Perligata::DATA' },
296             );
297             multibless %streams, 'Literal', 'DATIVE';
298              
299             my %matchops =
300             (
301             'compara' => { perl => 'm' },
302             'substitue' => { perl => 's' },
303             'converte' => { perl => 'tr' },
304             );
305              
306             multibless %matchops, 'MatchOperator', 'SUBNAME';
307             addres %matchops, 'MatchOperator', 'SUBNAME';
308              
309             my %ops =
310             (
311             'adde' => { perl => '+' },
312             'deme' => { perl => '-' },
313             'multiplica' => { perl => '*' },
314             'itera' => { perl => 'x' },
315             'divide' => { perl => '/' },
316             'recide' => { perl => '%' },
317             'eleva' => { perl => '**' },
318             'consocia' => { perl => '&' },
319             'interseca' => { perl => '|' },
320             'discerne' => { perl => '^' },
321             'depone' => { perl => '' , prefix => 1 },
322             );
323              
324             $ops{$_}{operator} = 1 foreach keys %ops;
325             multibless %ops, 'Operator', 'SUBNAME_A';
326             addres %ops, 'Operator', 'SUBNAME_A';
327              
328             my %lops =
329             (
330             'da' => { perl => '=', operator => 1 },
331             );
332              
333             multibless %lops, 'Operator', 'SUBNAME_AD';
334             addres %lops, 'Operator', 'SUBNAME_AD';
335              
336             my %invarops =
337             (
338             'atque' => { perl => '&&' },
339             'vel' => { perl => '||' },
340             'praestantiam' => { perl => '<' },
341             'non praestantiam' => { perl => '>=' },
342             'praestantias' => { perl => 'lt' },
343             'non praestantias' => { perl => 'ge' },
344             'aequalitam' => { perl => '==' },
345             'non aequalitam' => { perl => '!=' },
346             'aequalitas' => { perl => 'eq' },
347             'non aequalitas' => { perl => 'ne' },
348             'comparitiam' => { perl => '<=>' },
349             'comparitias' => { perl => 'cmp' },
350             'non comparitiam' => { perl => '!<=>' },
351             'non comparitias' => { perl => '!cmp' },
352              
353             'non' => { perl => '!', prefix => 1 },
354             'nega' => { perl => '-', prefix => 1 },
355             );
356              
357             $invarops{$_}{operator} = 1 foreach keys %invarops;
358             multibless %invarops, 'Operator', 'SUBNAME_A_ACCUSATIVE';
359              
360             my %connectives =
361             (
362             'que' => { perl => 'and' },
363             've' => { perl => 'or' },
364             );
365              
366             @{$connectives{$_}}{'operator','raw'} = (1,-$_) foreach keys %connectives;
367             multibless %connectives, 'Operator', 'CONNECTIVE';
368              
369             my %funcs_td =
370             (
371             'excerpe' => { perl => 'substr' }, # SPECIAL: SEE BELOW
372              
373             'cumula' => { perl => 'push' },
374             'capita' => { perl => 'unshift' },
375             'iunge' => { perl => 'splice' },
376             'digere' => { perl => 'sort' },
377             'retexe' => { perl => 'reverse' },
378             'evolute' => { perl => 'open' },
379             'lege' => { perl => 'read' },
380             'scribe' => { perl => 'print' },
381             'describe' => { perl => 'printf' },
382             'subscribe' => { perl => 'write' },
383             'conquire' => { perl => 'seek' },
384             'benedice' => { perl => 'bless' },
385             'liga' => { perl => 'tie' },
386             'modera' => { perl => 'fcntl' },
387             'conflue' => { perl => 'flock' },
388             'impera' => { perl => 'ioctl' },
389             'trunca' => { perl => 'truncate' },
390             );
391              
392             multibless %funcs_td, 'Function', 'SUBNAME_AD';
393             addres %funcs_td, 'Function', 'SUBNAME_AD';
394              
395             # "bless" HAS A COMMON LATIN CONTRACTION...
396              
397             $funcs_td{benedictum} = $funcs_td{benedicementum};
398             $funcs_td{benedicto} = $funcs_td{benedicemento};
399              
400             # THE FOLLOWING IS LVALUABLE, SO ARG MUST AGREE IN CASE WITH FUNCTION'S CASE...
401              
402             for (qw( excerpe )) {
403             $funcs_td{"${_}mentum"}{lex} = 'SUBNAME_A_ACCUSATIVE';
404             }
405              
406              
407             my %funcs_bd =
408             (
409             'vanne' => { perl => 'grep' },
410             'applica' => { perl => 'map' },
411             'digere' => { perl => 'sort' },
412             );
413              
414             multibless %funcs_bd, 'Function', 'SUBNAME_AB';
415             addres %funcs_bd, 'Function', 'SUBNAME_AB';
416              
417             my %funcs_b =
418             (
419             'factorem' => { perl => 'sub' },
420             'factori' => { perl => 'sub' },
421             );
422              
423             multibless %funcs_b, 'Function', 'SUBNAME_B_ACCUSATIVE';
424             $funcs_b{'factori'}{lex} = 'SUBNAME_B_DATIVE';
425              
426              
427             my %funcs_t =
428             (
429             'morde' => { perl => 'chomp' },
430             'praecide' => { perl => 'chop' },
431             'stude' => { perl => 'study' },
432             'iani' => { perl => 'undef' },
433             'lusta' => { perl => 'reset' },
434             'decumula' => { perl => 'pop' },
435             'decapita' => { perl => 'shift' },
436             'claude' => { perl => 'close' },
437             'perlege' => { perl => 'Lingua::Romana::Perligata::getline' },
438             'sublege' => { perl => 'getc' },
439             'enunta' => { perl => 'tell' },
440             'dele' => { perl => 'delete' },
441             'quisque' => { perl => 'each' },
442             'adfirma' => { perl => 'exists' },
443             'solvere' => { perl => 'untie' },
444             'preincresce' => { perl => '++', prefix => 1, operator => 1 },
445             'postincresce' => { perl => '++', prefix => 0, operator => 1 },
446             'predecresce' => { perl => '--', prefix => 1, operator => 1 },
447             'postdecresce' => { perl => '--', prefix => 0, operator => 1 },
448              
449             'reperi' => { perl => 'pos' }, # SPECIAL: SEE BELOW
450             'nomina' => { perl => 'keys' }, # SPECIAL: SEE BELOW
451             );
452              
453             multibless %funcs_t, 'Function', 'SUBNAME_D';
454             addres %funcs_t, 'Function', 'SUBNAME_D';
455              
456              
457             # THE FOLLOWING ARE LVALUABLE, SO ARG MUST AGREE IN CASE WITH FUNCTION'S CASE...
458              
459             for (qw( reperi nomina )) {
460             $funcs_t{"${_}mentum"}{lex} = 'SUBNAME_A_ACCUSATIVE';
461             }
462              
463             my %funcs_d =
464             (
465             'admeta' => { perl => 'Lingua::Romana::Perligata::__lastelem__' },
466             'inque' => { perl => 'Lingua::Romana::Perligata::__enquote__' },
467             'conscribe' => { perl => 'Lingua::Romana::Perligata::__enlist__' },
468             'come' => { perl => 'Lingua::Romana::Perligata::__beautify__' },
469             'priva' => { perl => 'abs' },
470             'angula' => { perl => 'atan2' },
471             'oppone' => { perl => 'sin' },
472             'accuba' => { perl => 'cos' },
473             'decolla' => { perl => 'int' },
474             'succide' => { perl => 'log' },
475             'fode' => { perl => 'sqrt' },
476             'conice' => { perl => 'rand' },
477             'prosemina' => { perl => 'srand' },
478             'inde' => { perl => 'chr' },
479             'senidemi' => { perl => 'hex' },
480             'octoni' => { perl => 'oct' },
481             'numera' => { perl => 'ord' },
482             'deminue' => { perl => 'lc' },
483             'minue' => { perl => 'lcfirst' },
484             'amplifica' => { perl => 'uc' },
485             'amplia' => { perl => 'ucfirst' },
486             'excipe' => { perl => 'quotemeta' },
487             'huma' => { perl => 'crypt' },
488             'meta' => { perl => 'length' },
489             'convasa' => { perl => 'pack' },
490             'deconvasa' => { perl => 'unpack' },
491             'scinde' => { perl => 'split' },
492             'scruta' => { perl => 'index' },
493             'coniunge' => { perl => 'join' },
494             'confirma' => { perl => 'defined' },
495             'secerna' => { perl => 'scalar' },
496             'argue' => { perl => 'values' },
497             'extremus' => { perl => 'eof' },
498             'deside' => { perl => 'wantarray' },
499             'aestima' => { perl => 'eval' },
500             'exi' => { perl => 'exit' },
501             'redde' => { perl => 'return' },
502             'mori' => { perl => 'die' },
503             'mone' => { perl => 'warn' },
504             'coaxa' => { perl => 'Carp::croak' },
505             'carpe' => { perl => 'Carp::carp' },
506             'memora' => { perl => 'caller' },
507             'agnosce' => { perl => 'ref' },
508             'exhibere' => { perl => 'tied' },
509             'require' => { perl => 'require' },
510             'demigrare' => { perl => 'chdir' },
511             'permitte' => { perl => 'chmod' },
512             'vende' => { perl => 'chown' },
513             'inveni' => { perl => 'glob' },
514             'copula' => { perl => 'link' },
515             'decopula' => { perl => 'unlink' },
516             'aedifica' => { perl => 'mkdir' },
517             'renomina' => { perl => 'rename' },
518             'excide' => { perl => 'rmdir' },
519             'exprime' => { perl => 'stat' },
520             'terre' => { perl => 'alarm' },
521             'mitte' => { perl => 'dump' },
522             'commuta' => { perl => 'exec' },
523             'furca' => { perl => 'fork' },
524             'interfice' => { perl => 'kill' },
525             'dormi' => { perl => 'sleep' },
526             'obsecra' => { perl => 'system' },
527             'dissimula' => { perl => 'umask' },
528             'manta' => { perl => 'wait' },
529              
530             'inscribe' => { perl => ':' }, # SPECIAL: SEE BELOW
531             'arcesse' => { perl => '${' }, # SPECIAL: SEE BELOW
532              
533             'sere' => { perl => 'Lingua::Romana::Perligata::__encatenate__' },
534             );
535              
536             multibless %funcs_d, 'Function', 'SUBNAME_A';
537             addres %funcs_d, 'Function', 'SUBNAME_A';
538              
539             # 'inscribe' IS SPECIAL: ONLY IMPERATIVE
540              
541             delete @funcs_d{grep /^inscribement/, keys %funcs_d};
542              
543             # 'arcesse' IS SPECIAL: NO IMPERATIVE AND EXTRA (PSEUDO-)RESULTATIVES
544              
545             delete $funcs_d{'arcesse'};
546              
547             # SCALAR AS NON-TERMINAL INDEX
548              
549             $funcs_d{'arcessementi'} = { %{$funcs_d{'arcessementum'}},
550             lex => 'SUBNAME_A_GENITIVE' };
551              
552             # ARRAY DEREFERENCE
553              
554             $funcs_d{'arcessementa'}{perl} = '@{';
555             $funcs_d{'arcessementis'}{perl} = '@{';
556             $funcs_d{'arcessementorum'} = { %{$funcs_d{'arcessementa'}},
557             lex => 'SUBNAME_A_GENITIVE' };
558              
559             $funcs_d{'arcessementus'} = { %{$funcs_d{'arcessementa'}}, perl => '%{' };
560             $funcs_d{'arcessementibus'} = { %{$funcs_d{'arcessementis'}}, perl => '%{' };
561             $funcs_d{'arcessementuum'} = { %{$funcs_d{'arcessementus'}},
562             lex => 'SUBNAME_A_GENITIVE'};
563              
564              
565             my %funcs_dl =
566             (
567             'adi' => { perl => 'goto' },
568             'confectus' => { perl => 'continue' },
569             'domus' => { perl => 'package' },
570             'legatarius' => { perl => 'use base' },
571             'ute' => { perl => 'use' },
572             );
573              
574             multibless %funcs_dl, 'Function_Lit', 'SUBNAME_A';
575             addres %funcs_dl, 'Function_Lit', 'SUBNAME_A';
576              
577             my %funcs_dlo =
578             (
579             'ultimus' => { perl => 'last' },
580             'posterus' => { perl => 'next' },
581             'reconnatus' => { perl => 'redo' },
582             );
583              
584             multibless %funcs_dlo, 'Function_Lit', 'SUBNAME_OA';
585             # addres %funcs_dl, 'Function_Lit', 'SUBNAME_OA';
586              
587             my %misc =
588             (
589             'fac' => { lex => 'DO', perl => 'do' },
590             'per' => { lex => 'FOR', perl => 'for' },
591             'per quisque' => { lex => 'FOR', perl => 'foreach' },
592             'si' => { lex => 'CONTROL', perl => 'if' },
593             'nisi' => { lex => 'CONTROL', perl => 'unless' },
594             'donec' => { lex => 'CONTROL', perl => 'until' },
595             'dum' => { lex => 'CONTROL', perl => 'while' },
596             'cum' => { lex => 'WITH', perl => '' },
597             'intra' => { lex => 'WITHIN', perl => '::' },
598             'apud' => { lex => 'ARROW', perl => '->' },
599             'tum' => { lex => 'COMMA', perl => ',' },
600             'in' => { lex => 'IN', perl => '' },
601             'sic' => { lex => 'BEGIN', perl => '{' },
602             'cis' => { lex => 'END', perl => '}' },
603             'princeps' => { lex => 'NAME', raw=>'main',perl => 'main' },
604             'ad' => { lex => 'ADDRESS', perl => '\\' },
605             );
606              
607             my %tokens =
608             (
609             %literals, %numerals, %underscores,
610             %numerals, %ordinals_a, %ordinals_d,
611             %varmods, %matchops, %ops, %lops, %invarops,
612             %funcs_td, %funcs_bd, %funcs_b, %funcs_t,
613             %funcs_d, %funcs_dl, %funcs_dlo,
614             %misc, %streams,
615             );
616              
617             # Handle likely captialization variations...
618             @tokens{map {ucfirst} keys %tokens} = values %tokens;
619              
620             foreach my $key ( keys %tokens )
621             {
622             $tokens{$key}{raw} = $key
623             unless $tokens{$key}{raw};
624             }
625              
626             my $distokens = join '|', reverse sort keys %tokens;
627             my $tokens = "\\A\\s*($distokens)\\b";
628             my $tokensque = "\\A\\s*($distokens)que\\b";
629             my $tokensve = "\\A\\s*($distokens)ve\\b";
630              
631             sub token($$$$)
632             {
633 16     16 0 46 my ($raw, $lex, $perl, $blesstype) = @_;
634 16         74 return bless { raw => $raw, lex => $lex, perl => $perl }, $blesstype;
635             }
636              
637             sub tokdup
638             {
639 38     38 0 68 my ($archetype) = @_;
640 38         271 bless { %$archetype }, $archetype->{lex};
641             }
642              
643             sub tokenize
644             {
645 4     4 0 11 my ($text) = @_;
646 4         7 my @tokens;
647 4         7 my $bad = "";
648 4         13 my $lines = $text =~ tr/\n/\n/;
649              
650 4         14 while (length $text)
651             {
652 56         174 $text =~ s/\A\s+//;
653 56         112 my $line = $lines - ($text =~ tr/\n/\n/) + $offset;
654 56 50 33     24314 if ($text =~ s/\A(adnota.*)//i)
    50 33        
    50 100        
    50          
    50          
    50          
    50          
    100          
    50          
    50          
    100          
    50          
    50          
    50          
    50          
    50          
    50          
    100          
    50          
    50          
    50          
    50          
    50          
    50          
    50          
    100          
    100          
    50          
    100          
    100          
655             {
656             # ignore comments
657             }
658             elsif ($text =~ s/\A(nuntius|finis)[ \t]*[.]?[ \t]*\n(.*)//smi)
659             {
660             # set up DATA stream
661 2     2   23 use vars '*DATA';
  2         2  
  2         6130  
662 0         0 local *Lingua::Romana::Perlidata::DATASRC;
663 0         0 my $pipe = pipe \*Lingua::Romana::Perligata::DATA,
664             \*Lingua::Romana::Perlidata::DATASRC;
665              
666 0         0 print Lingua::Romana::Perlidata::DATASRC $2;
667             }
668             elsif ($text =~ s/\Adic(?:emen)?tum(que|ve|)\s+sic\s+\b(.*?)\s+cis\b//si)
669             {
670 0 0       0 push @tokens, tokdup $connectives{lc $1} if $1;
671 0         0 push @tokens, token($2,'NAME',"$2",'Name');
672             }
673             elsif ($text =~ s/\Asic(que|ve|)\s+(.*?)\s+cis\s+dic(?:emen)?tum\b//si)
674             {
675 0 0       0 push @tokens, tokdup $connectives{lc $1} if $1;
676 0         0 push @tokens, token($2,'NAME',"$2",'Name');
677             }
678             elsif ($text =~ s/\A(atque|vel)\b//i)
679             {
680 0         0 push @tokens, tokdup $misc{'tum'};
681 0         0 push @tokens, tokdup $invarops{lc $1};
682             }
683             elsif ($text =~ s/\A(($roman)im(?:o|ae)(que|ve|))\b//ix && length $2)
684             {
685 0 0       0 push @tokens, tokdup $connectives{lc $+} if $+;
686 0         0 push @tokens, token($1,'ORDINAL_DATIVE',from_rn($2),'ORDINAL_DATIVE');
687             }
688             elsif ($text =~ s/\A(($roman)im(?:um|os|am|as)(que|ve|))\b//ix && length $2)
689             {
690 0 0       0 push @tokens, tokdup $connectives{lc $+} if $+;
691 0         0 push @tokens, token($1,'ORDINAL',from_rn($2),'ORDINAL');
692             }
693             elsif ($text =~ s/\A(($roman)(que|ve|))\b//ix && length $2)
694             {
695 2 50       22 push @tokens, tokdup $connectives{lc $+} if $+;
696 2         12 push @tokens, token($1,'NUMERAL',from_rn($2),'NUMERAL');;
697             }
698             elsif ($text =~ s/$tokensque//i)
699             {
700 0         0 push @tokens, tokdup $connectives{'que'};
701 0         0 push @tokens, tokdup $tokens{lc $1};
702             }
703             elsif ($text =~ s/$tokensve//i)
704             {
705 0         0 push @tokens, tokdup $connectives{'ve'};
706 0         0 push @tokens, tokdup $tokens{lc $1};
707             }
708             elsif ($text =~ s/$tokens//i)
709             {
710 38         155 push @tokens, tokdup $tokens{lc $1};
711             }
712             elsif ($text =~ s/\A(([a-z_][0-9a-z_]*?)(um|)ementum)(que|ve|)((?:\s+)sicut)?\b//i)
713             {
714 0 0       0 if ($5)
715             {
716 0 0       0 my $token = $4 ? $1.$4 : $1;
717 0         0 push @tokens, token($token,'NAME',$token,'Name');
718             }
719             else
720             {
721 0 0       0 push @tokens, tokdup $connectives{lc $4} if $4;
722 0 0       0 my $perl = $3 ? "\$$2->" : $2;
723 0         0 push @tokens, token($1,'SUBNAME_OA_ACCUSATIVE',$perl,'Literal');
724             }
725             }
726             elsif ($text =~ s/\A(([a-z_][0-9a-z_]*?)(um|)ementa)(que|ve|)((?:\s+)sicut)?\b//i)
727             {
728 0 0       0 if ($5)
729             {
730 0 0       0 my $token = $4 ? $1.$4 : $1;
731 0         0 push @tokens, token($token,'NAME',$token,'Name');
732             }
733             else
734             {
735 0 0       0 push @tokens, tokdup $connectives{lc $4} if $4;
736 0 0       0 my $perl = $3 ? "\$$2->" : $2;
737 0         0 push @tokens, token($1,'SUBNAME_OA_ACCUSATIVE',$perl,'Literal');
738             }
739             }
740             elsif ($text =~ s/\A(([a-z_][0-9a-z_]*?)(um|)emento)(que|ve|)((?:\s+)sicut)?\b//i)
741             {
742 0 0       0 if ($5)
743             {
744 0 0       0 my $token = $4 ? $1.$4 : $1;
745 0         0 push @tokens, token($token,'NAME',$token,'Name');
746             }
747             else
748             {
749 0 0       0 push @tokens, tokdup $connectives{lc $4} if $4;
750 0 0       0 my $perl = $3 ? "\$$2->" : $2;
751 0         0 push @tokens, token($1,'SUBNAME_OA_DATIVE',$perl,'Literal');
752             }
753             }
754             elsif ($text =~ s/\A(([a-z_][0-9a-z_]*?)(um|)ementis)(que|ve|)((?:\s+)sicut)?\b//i)
755             {
756 0 0       0 if ($5)
757             {
758 0 0       0 my $token = $4 ? $1.$4 : $1;
759 0         0 push @tokens, token($token,'NAME',"$perl",'Name');
760             }
761             else
762             {
763 0         0 $bad .= "'-mentis' illicitum: '$1'"
764             . adversum({line=>$line});
765             # One day this may be:
766             #
767             # push @tokens, tokdup $connectives{lc $4} if $4;
768             # my $perl = $3 ? "\$$2->" : $2;
769             # push @tokens, token($1,'SUBNAME_OA_DATIVE',$perl,'Literal');
770             }
771             }
772             elsif ($text =~ s/\A(([a-z_][0-9a-z_]*)orum)(que|ve|)((?:\s+)sicut)?\b//i)
773             {
774 0 0       0 if ($4)
775             {
776 0 0       0 my $token = $3 ? $1.$3 : $1;
777 0         0 push @tokens, token($token,'NAME',$token,'Name');
778             }
779             else
780             {
781 0 0       0 push @tokens, tokdup $connectives{lc $3} if $3;
782 0         0 push @tokens, token($1,'GENITIVE',"\@$2",'Literal');
783             }
784             }
785             elsif ($text =~ s/\A(([a-z_][0-9a-z_]*)uum)(que|ve|)((?:\s+)sicut)?\b//i)
786             {
787 0 0       0 if ($4)
788             {
789 0 0       0 my $token = $3 ? $1.$3 : $1;
790 0         0 push @tokens, token($token,'NAME',$token,'Name');
791             }
792             else
793             {
794 0 0       0 push @tokens, tokdup $connectives{lc $3} if $3;
795 0         0 push @tokens, token($1,'GENITIVE',"\%$2",'Literal');
796             }
797             }
798             elsif ($text =~ s/\A(([a-z_][0-9a-z_]*)um)(que|ve|)((?:\s+)sicut)?\b//i)
799             {
800 4 50       18 if ($4)
801             {
802 0 0       0 my $token = $3 ? $1.$3 : $1;
803 0         0 push @tokens, token($token,'NAME',$token,'Name');
804             }
805             else
806             {
807 4 50       12 push @tokens, tokdup $connectives{lc $3} if $3;
808 4         17 push @tokens, token($1,'ACCUSATIVE',"\$$2",'Literal');
809             }
810             }
811             elsif ($text =~ s/\A(([a-z_][0-9a-z_]*)a)(que|ve|)((?:\s+)sicut)?\b//i)
812             {
813 0 0       0 if ($4)
814             {
815 0 0       0 my $token = $3 ? $1.$3 : $1;
816 0         0 push @tokens, token($token,'NAME',$token,'Name');
817             }
818             else
819             {
820 0 0       0 push @tokens, tokdup $connectives{lc $3} if $3;
821 0         0 push @tokens, token($1,'ACCUSATIVE',"\@$2",'Literal');
822             }
823             }
824             elsif ($text =~ s/\A(([a-z_][0-9a-z_]*)ibus)(que|ve|)((?:\s+)sicut)?\b//i)
825             {
826 0 0       0 if ($4)
827             {
828 0 0       0 my $token = $3 ? $1.$3 : $1;
829 0         0 push @tokens, token($token,'NAME',$token,'Name');
830             }
831             else
832             {
833 0 0       0 push @tokens, tokdup $connectives{lc $3} if $3;
834 0         0 push @tokens, token($1,'DATIVE',"\%$2",'Literal');
835             }
836             }
837             elsif ($text =~ s/\A(([a-z_][0-9a-z_]*)us)(que|ve|)((?:\s+)sicut)?\b//i)
838             {
839 0 0       0 if ($4)
840             {
841 0 0       0 my $token = $3 ? $1.$3 : $1;
842 0         0 push @tokens, token($token,'Name',$token,'Name');
843             }
844             else
845             {
846 0 0       0 push @tokens, tokdup $connectives{lc $3} if $3;
847 0         0 push @tokens, token($1,'ACCUSATIVE',"\%$2",'Literal');
848             }
849             }
850             elsif ($text =~ s/\A(([a-z_][0-9a-z_]*)o)(que|ve|)((?:\s+)sicut)?\b//i)
851             {
852 0 0       0 if ($4)
853             {
854 0 0       0 my $token = $3 ? $1.$3 : $1;
855 0         0 push @tokens, token($token,'NAME',$token,'Name');
856             }
857             else
858             {
859 0 0       0 push @tokens, tokdup $connectives{lc $3} if $3;
860 0         0 push @tokens, token($1,'DATIVE',"\$$2",'Literal');
861             }
862             }
863             elsif ($text =~ s/\A(([a-z_][0-9a-z_]*)is)(que|ve|)((?:\s+)sicut)?\b//i)
864             {
865 0 0       0 if ($4)
866             {
867 0 0       0 my $token = $3 ? $1.$3 : $1;
868 0         0 push @tokens, token($token,'NAME',$token,'Name');
869             }
870             else
871             {
872 0 0       0 push @tokens, tokdup $connectives{lc $3} if $3;
873 0         0 push @tokens, token($1,'DATIVE',"\@$2",'Literal');
874             }
875             }
876             elsif ($text =~ s/\A(([a-z_][0-9a-z_]*)tori)(que|ve|)((?:\s+)sicut)?\b//i)
877             {
878 0 0       0 if ($4)
879             {
880 0 0       0 my $token = $3 ? $1.$3 : $1;
881 0         0 push @tokens, token($token,'NAME',$token,'Name');
882             }
883             else
884             {
885 0 0       0 push @tokens, tokdup $connectives{lc $3} if $3;
886 0         0 push @tokens, token($1,'DATIVE',"\\&$2",'Literal');
887             }
888             }
889             elsif ($text =~ s/\A(([a-z_][0-9a-z_]*)i)(que|ve|)((?:\s+)sicut)?\b//i)
890             {
891 0 0       0 if ($4)
892             {
893 0 0       0 my $token = $3 ? $1.$3 : $1;
894 0         0 push @tokens, token($token,'NAME',$token,'Name');
895             }
896             else
897             {
898 0 0       0 push @tokens, tokdup $connectives{lc $3} if $3;
899 0         0 push @tokens, token($1,'GENITIVE',"\$$2",'Literal');
900             }
901             }
902             elsif ($text =~ s/\A(([a-z_][0-9a-z_]*)ere)(que|ve|)((?:\s+)sicut)?\b//i)
903             {
904 2 50       9 if ($4)
905             {
906 0 0       0 my $token = $3 ? $1.$3 : $1;
907 0         0 push @tokens, token($token,'NAME',$token,'Name');
908             }
909             else
910             {
911 2 50       39 push @tokens, tokdup $connectives{lc $3} if $3;
912 2         7 push @tokens, token($1,'INFINITIVE',"$2",'Literal');
913             }
914             }
915             elsif ($text =~ s/\A(([a-z_][0-9a-z_]*?)(um|)e)(que|ve|)((?:\s+)sicut)?\b//i)
916             {
917 2 50       9 if ($5)
918             {
919 0 0       0 my $token = $4 ? $1.$4 : $1;
920 0         0 push @tokens, token($token,'NAME',$token,'Name');
921             }
922             else
923             {
924 2 50       39 push @tokens, tokdup $connectives{lc $4} if $4;
925 2 50       9 my $perl = $3 ? "\$$2->" : $2;
926 2         7 push @tokens, token($1,'SUBNAME',$perl,'Literal');
927             }
928             }
929             elsif ($text =~ s/\A(([a-z_][0-9a-z_]*)torem)(que|ve|)((?:\s+)sicut)?\b//i)
930             {
931 0 0       0 if ($4)
932             {
933 0 0       0 my $token = $3 ? $1.$3 : $1;
934 0         0 push @tokens, token($token,'NAME',$token,'Name');
935             }
936             else
937             {
938 0 0       0 push @tokens, tokdup $connectives{lc $3} if $3;
939 0         0 push @tokens, token($1,'ACCUSATIVE',"\\&$2",'Literal');
940             }
941             }
942             elsif ($text =~ s/\A([.])//)
943             {
944 4         15 push @tokens, token($1,'PERIOD',";",'Separator');
945             }
946             elsif ($text =~ s/\A(\S+)(que|ve|)((?:\s+)sicut)?\b//)
947             {
948 2 50       8 if ($3)
949             {
950 0 0       0 my $token = $2 ? $1.$2 : $1;
951 0 0       0 push @tokens, token($token,'NAME',$token,'Name') if $token;
952             }
953             else
954             {
955 2 50       6 push @tokens, tokdup $connectives{lc $2} if $2;
956 2 50       14 push @tokens, token($1,'NAME',"$1",'Name') if $1;
957             }
958             }
959             else
960             {
961 2 50       13 $text =~ s/\A(\S+)// or next;
962 0         0 my $error = $1;
963 0         0 $bad .= "Aliquod barbarum inveni: '$error'"
964             . adversum({line=>$line});
965             }
966 54 50       927 $tokens[-1]->{line} = $line if @tokens;
967             }
968 4 50 33     22 if (($lex ||$debug) && @tokens) {
      33        
969 0         0 my $format = "%-20s %-10s %-20s\n";
970 0         0 printf $format, qw(Word Role Meaning);
971 0         0 printf $format, qw(==== ==== =======);
972 0         0 foreach ( @tokens ) {
973 0         0 printf $format, @{$_}{qw(raw lex perl)};
  0         0  
974             }
975 0         0 print "\n", "="x72, "\n\n";
976 0 0       0 die "\n" if $lex;
977             }
978              
979 4 50       9 die $bad if $bad;
980 4         17 return [@tokens];
981             }
982              
983 2     2   22 use Carp;
  2         5  
  2         11367  
984              
985             sub conn_command {
986 10     10 0 20 my ($toks, $eatend, $noeatend) = @_;
987 10         29 my $command = &command;
988 10   66     38 while (@$toks && $toks->[0]{lex} eq 'CONNECTIVE') {
989 0         0 local $Lingua::Romana::Perligata::connective = shift @$toks;
990 0         0 $connective->{L} = $command;
991 0         0 $connective->{R} = &command;
992 0         0 $command = $connective;
993             }
994 10         23 return $command;
995             }
996              
997              
998             sub command {
999 10     10 0 17 my ($toks, $eatend, $noeatend) = @_;
1000 10         27 my @Astack = { data => [], complete => 1 }; #SENTINEL ACCUSATIVE FRAME
1001 10         14 my (@Bstack, @Dstack, @Vstack, $Vdone);
1002 10         15 my $Dindir = 0;
1003 10         13 my @lastsubstantive;
1004 10         14 my $empty = 1;
1005              
1006 10         11 my $reduce;
1007             my $Astack_push = sub {
1008 14 100   14   24 if ($Astack[-1]{complete}) {
1009 6 50       14 $reduce->($_[0]) if @Astack > 1;
1010 6         17 push @Astack, { data => [ $_[0] ] };
1011             }
1012             else {
1013 8         10 push @{$Astack[-1]{data}}, $_[0];
  8         19  
1014             }
1015 14         22 $Astack[-1]{complete} = 1;
1016 10         39 };
1017              
1018             $reduce = sub {
1019 24     24   36 my ($lookahead) = @_;
1020 24 100       42 if (! @Vstack) {
1021 12 50 50     39 if (@Dstack && ($Dstack[-1]{V}{lex}||"") eq 'OWNER_D') {
      66        
1022 0         0 $Vdone = pop @Dstack;
1023 0         0 return 1
1024             }
1025 12         23 return 0;
1026             }
1027 12 100 100     96 return 0 if $Vstack[-1]{lex} =~ /^SUBNAME_?A?D?$/ && $lookahead->{lex} !~ /PERIOD|DO|END|CONNECTIVE/
      66        
1028             || ref $Vstack[-1] eq "STATEMENT";
1029 6         9 my $verb = $Vstack[-1];
1030 6         22 my ($needA, $needD) = $verb->{lex} =~ /_(O?A?)([BD])?/;
1031 6 50 66     22 $needA ||= $verb->{lex} eq 'SUBNAME' ? "OA" : "";
1032 6   100     31 $needD ||= "";
1033             return 0 if $needA && $needA ne "OA" && (@Astack<=1 || !$Astack[-1]{complete})
1034 6 50 66     56 || $needD eq 'D' && !@Dstack
      33        
      66        
      66        
      33        
      33        
      33        
1035             || $needD eq 'B' && !@Bstack;
1036 6 50       15 my $dat = $needD eq 'D' ? pop(@Dstack)
    100          
1037             : $needD eq 'B' ? pop(@Bstack)
1038             : undef;
1039 6 50 33     23 my $acc = $needA && @Astack>1 ? pop(@Astack)->{data} : undef;
1040 6         26 my $statement = bless { V=>pop(@Vstack), A=>$acc, D=>$dat }, "STATEMENT";
1041 6 50 33     54 if ($verb->{lex} =~ /SUBNAME_.*_ACCUSATIVE/
    100 33        
    50          
1042             || $Dindir && $verb->{lex} =~ /SUBNAME_.*_DATIVE|OWNER_D/ )
1043             {
1044 0 0       0 if ($verb->{lex} =~ /SUBNAME_.*_DATIVE|OWNER_D/) {
1045 0         0 $statement->{R} = $Dindir;
1046 0         0 $Dindir = 0;
1047             }
1048 0         0 $Astack_push->($statement);
1049 0         0 push @lastsubstantive, $Astack[-1]{data};
1050             }
1051             elsif ($verb->{lex} =~ /SUBNAME_.*_DATIVE|OWNER_D/ ) {
1052 2         5 push @Dstack, $statement;
1053 2         5 push @lastsubstantive, \@Dstack;
1054             }
1055             elsif ($verb->{lex} =~ /SUBNAME_.*_GENITIVE/ ) {
1056 0         0 my $lastsubstantive = pop @lastsubstantive;
1057 0 0       0 die "Genitivum non junctum: '$ord->{raw}'"
1058             . adversum($verb)
1059             unless $lastsubstantive;
1060 0         0 $ord = $lastsubstantive[-1][-1];
1061             die "Index '$ord->{raw}' ordinalis non est"
1062             . adversum($ord)
1063 0 0 0     0 if $ord->{lex} && $ord->{lex} eq 'NUMERAL';
1064 0         0 push @{$ord->{G}}, $statement;
  0         0  
1065 0         0 push @lastsubstantive, $Astack[-1]{data};
1066             }
1067 4         6 else { $Vdone = $statement }
1068 6 50       13 if ($debug) {
1069 0         0 print "reduced: ", Data::Dumper->Dump([$statement]);
1070             }
1071 6         24 return 1;
1072 10         41 };
1073              
1074 10         14 my $tok;
1075 10         23 while ( $tok = $toks->[0] ) {
1076 42 50       57 if ($debug) {
1077 0         0 print "Next: '$toks->[0]{raw}' ($toks->[0]{lex}):\n";
1078             }
1079 42 100 33     426 if ($tok->{lex} =~ /^(NUMERAL|ORDINAL)$/
    50 66        
    50 33        
    100 66        
    50 33        
    50 0        
    100          
    100          
    50          
    100          
    50          
    50          
    100          
    50          
    100          
    100          
    100          
    50          
    50          
    0          
1080             || $Dindir && $tok->{lex} eq 'ORDINAL_DATIVE')
1081 6         7 { shift @$toks;
1082 6 50 66     28 if ($1 eq 'NUMERAL' && $toks->[0]{lex} eq 'ORDINAL') {
1083 0         0 $tok->{raw} .= " " . $toks->[0]{raw};
1084 0         0 $tok->{perl} /= $toks->[0]{perl};
1085 0         0 shift @$toks;
1086             }
1087 6 50       13 if ($tok->{lex} eq 'ORDINAL_DATIVE') {
1088 0         0 $tok->{R} = $Dindir;
1089 0         0 $Dindir = 0;
1090             }
1091 6         14 $Astack_push->($tok);
1092 6         8 $lastownable = $Astack[-1]{data};
1093 6         8 push @lastsubstantive, $lastownable;
1094             }
1095             elsif ($tok->{lex} eq 'ORDINAL_DATIVE')
1096 0         0 { shift @$toks;
1097 0         0 $reduce->($tok);
1098 0         0 push @Dstack, $tok;
1099 0         0 $lastownable = \@Dstack;
1100 0         0 push @lastsubstantive, $lastownable;
1101             }
1102             elsif ($tok->{lex} eq 'WITH') {
1103 0         0 push @Astack, { data=>[], complete=>0 };
1104 0         0 shift @$toks;
1105             }
1106             elsif ($tok->{lex} =~ /^(?:ACCUSATIVE|NAME)$/
1107             || $Dindir && $tok->{lex} eq 'DATIVE')
1108 8 50       20 { if ($tok->{lex} eq 'DATIVE') {
1109 0         0 $tok->{R} = $Dindir;
1110 0         0 $Dindir = 0;
1111             }
1112 8         17 $Astack_push->($tok);
1113 8         9 shift @$toks;
1114 8         11 $lastownable = $Astack[-1]{data};
1115 8         13 push @lastsubstantive, $lastownable;
1116             }
1117             elsif ( $tok->{lex} eq 'ARROW' ) {
1118 0         0 my $owner = $toks->[1];
1119             my $perl = $owner->{perl} =~ /^\W/
1120             ? $owner->{perl}
1121 0 0       0 : "$owner->{raw}::";
1122             $lastownable->[-1]->{perl}
1123 0         0 =~ s{^}{$perl->};
1124 0         0 splice @$toks, 0, 2;
1125             }
1126             elsif ( $tok->{lex} eq 'WITHIN' ) {
1127 0         0 my $owner = $toks->[1];
1128             $lastownable->[-1]->{raw}
1129 0         0 =~ s{^(\W*)}{$1$owner->{raw}::};
1130             $lastownable->[-1]->{perl}
1131 0         0 =~ s{^(\W*)}{$1$owner->{raw}::};
1132 0         0 splice @$toks, 0, 2;
1133             }
1134             elsif ( $tok->{lex} eq 'GENITIVE' ) {
1135 2         6 $reduce->($tok);
1136 2         4 my $gen = shift @$toks;
1137 2 50       6 die "Genitivum indominum: '$ord->{raw}'"
1138             . adversum($gen)
1139             unless @lastsubstantive;
1140 2         5 $ord = $lastsubstantive[-1][-1];
1141             die "Index '$ord->{raw}' ordinalis non est"
1142             . adversum($ord)
1143 2 50 33     12 if $ord->{lex} && $ord->{lex} eq 'NUMERAL';
1144 2         4 push @{$ord->{G}}, $gen;
  2         6  
1145 2         4 $lastownable = $ord->{G};
1146             }
1147             elsif ( $tok->{lex} eq 'INFINITIVE' )
1148 2         7 { $reduce->($tok); $Vdone = subdefn($toks); last }
  2         6  
  2         3  
1149             elsif ( $tok->{lex} eq 'CONTROL' )
1150 0         0 { $reduce->($tok); $Vdone = control($toks, \@Bstack); last }
  0         0  
  0         0  
1151             elsif ( $tok->{lex} eq 'FOR' )
1152 2         25 { $reduce->($tok); $Vdone = for_control($toks, \@Bstack); last }
  2         7  
  2         4  
1153             elsif ( $tok->{lex} eq 'BEGIN' )
1154 0         0 { push @Bstack, block($toks) }
1155             elsif ( $tok->{lex} eq 'OWNER_D' )
1156 0         0 { $reduce->($tok); push @Vstack, $tok; shift @$toks; }
  0         0  
  0         0  
1157             elsif ( $tok->{lex} eq 'COMMA' )
1158             { $reduce->($tok)
1159             unless $lastownable && @Astack>1 &&
1160 8 100 33     40 $lastownable == $Astack[-1]{data};
      66        
1161             die "'$tok->{raw}' immaturum est " . adversum($tok)
1162 8 50 33     31 unless @Astack>1 && $Astack[-1]{complete};
1163 8         13 $Astack[-1]{complete} = 0;
1164 8         10 shift @$toks;
1165             }
1166             elsif ( $tok->{lex} eq 'ADDRESS' )
1167 0         0 { $reduce->($tok);
1168 0         0 $Dindir++;
1169 0         0 shift @$toks;
1170             }
1171             elsif ( $tok->{lex} eq 'DATIVE' )
1172 2         6 { $reduce->($tok); push @Dstack, $tok; shift @$toks;
  2         5  
  2         4  
1173 2         3 $lastownable = \@Dstack;
1174 2         5 push @lastsubstantive, $lastownable;
1175             }
1176             elsif ( $tok->{lex} =~ /^SUBNAME/ ) # WAS: /SUBNAME_
1177 6 50       12 { if ($Astack[-1]{complete}) {
    0          
1178 6         14 $reduce->($tok)
1179             }
1180             elsif ($tok->{perl} !~ /^(and|or|[&|]{2})$/) {
1181 0         0 push @Astack, { data=>[] };
1182             }
1183 6         9 push @Vstack, $tok; shift @$toks;
  6         39  
1184 6         12 $lastownable = \@Vstack;
1185             }
1186             # elsif ( $tok->{lex} =~ /^SUBNAME$/ )
1187             # { if ($Astack[-1]{complete}) {
1188             # $reduce->($tok)
1189             # }
1190             # elsif ($tok->{perl} !~ /^[&|]{2}$/ {
1191             # push @Astack, { data=>[] };
1192             # }
1193             # push @Vstack, $tok; shift @$toks;
1194             # $lastownable = \@Vstack;
1195             # }
1196             elsif ( $tok->{lex} =~ /PERIOD/ )
1197 4   33     9 { 1 while $reduce->($tok) && !$Vdone;
1198             $Vdone = pop(@Astack)->{data}
1199 4 50 0     12 if $Lingua::Romana::Perligata::connective
      33        
1200             && !($Vdone || @Astack <= 1);
1201 4         5 shift @$toks;
1202             last
1203 4         7 }
1204             elsif ( $tok->{lex} =~ /CONNECTIVE/ )
1205 0   0     0 { 1 while $reduce->($tok) && !$Vdone;
1206             $Vdone = pop(@Astack)->{data}
1207 0 0 0     0 unless $Vdone || @Astack <= 1;
1208 0         0 last }
1209             elsif ( $eatend && $tok->{lex} =~ /$eatend/ )
1210 2         5 { 1 while $reduce->($tok);
1211             $Vdone or $Vdone = @Astack > 1
1212             ? pop(@Astack)->{data}
1213 2 50       9 : pop @Dstack;
    50          
1214 2         4 shift @$toks; last }
  2         5  
1215             elsif ( $noeatend && $tok->{lex} =~ /$noeatend/ )
1216 0         0 { 1 while $reduce->($tok);
1217             $Vdone or $Vdone = pop(@Astack)->{data}
1218 0 0 0     0 || pop @Dstack;
1219 0         0 last }
1220             else {
1221 0         0 die "Non intellexi: '$tok->{raw}'" . adversum($tok);
1222             }
1223             }
1224             continue {
1225 32         40 $empty = 0;
1226 32 50       97 if ($debug) {
1227 0         0 print "After '$tok->{raw}' ($tok->{lex}):\n";
1228 0         0 print Data::Dumper->Dump([\@Vstack, \@Astack, \@Bstack, \@Dstack, \@lastsubstantive, \$lastownable, \$Vdone], [qw{Vstack Astack Bstack Dstack LastS LastO Vdone}]);
1229             }
1230             }
1231             die "Iussum nefastum: '$Vstack[-1]{raw}'" . adversum($Vstack[-1])
1232 10 0 33     35 . ( $Vstack[-1]{lex} !~ /(ACCUSATIVE|DATIVE)$/
    50          
1233             ? "(Vellesne '$Vstack[-1]{raw}mentum' unumve ceteri)\n"
1234             : "" )
1235             if $Vdone && @Vstack;
1236             die "Accusativum non junctum: '"
1237 0         0 . join(" tum ", map {$_->{raw}} @{$Astack[-1]{data}})
  0         0  
1238             . "'"
1239 10 50 33     96 . adversum($Astack[-1]{data}[0])
1240             if @Astack > 1 && !@Vstack;
1241 10 50 33     24 die "Dativum non junctum: '$Dstack[-1]{raw}'" . adversum($Dstack[-1])
1242             if @Dstack && !@Vstack;
1243 10 50 33     22 die "Sententia imperfecta prope '$tok->{raw}'" . adversum($tok)
1244             unless $Vdone || $empty;
1245 10 50       61 return $empty ? $tok
    100          
1246             : ref $Vdone eq 'ARRAY' ? $Vdone->[0]
1247             : $Vdone;
1248             }
1249              
1250             sub block
1251             {
1252 4     4 0 6 my ($toks) = @_;
1253 4         5 my @self;
1254 4         6 my $next = shift @$toks;
1255             die "Exspectavi 'sic' sed inveni '$next->{raw}'" . adversum($next)
1256 4 50       10 unless $next->{lex} eq 'BEGIN';
1257 4         12 while ($toks->[0]{lex} ne 'END') {
1258 4         9 my $command = conn_command($_[0],'PERIOD','END');
1259 4 50       17 push @self, $command if $command;
1260             }
1261 4         14 $next = shift @$toks;
1262             die "Exspectavi 'cis' sed inveni '$next->{raw}'" . adversum($next)
1263 4 50       11 unless $next->{lex} eq 'END';
1264 4         20 return bless \@self, 'BLOCK';
1265             }
1266              
1267             sub subdefn
1268             {
1269 2     2 0 5 my ($toks) = @_;
1270 2         4 my $self = shift @$toks;
1271 2         5 $self->{B} = block($toks);
1272 2         5 return $self;
1273             }
1274              
1275             sub for_control
1276             {
1277 2     2 0 5 my ($toks, $Bstack) = @_;
1278 2         4 my $self = shift @$toks;
1279 2         2 my $var;
1280             $var = $toks->[0]{lex} eq 'ACCUSATIVE'
1281             ? shift @$toks
1282             : $toks->[0]{lex} ne 'IN'
1283             ? die "Exspectavi accusativum post 'per' sed inveni '$toks->[0]{raw}'" . adversum($toks->[0])
1284 2 0       8 : tokdup $tokens{'huic'};
    50          
1285 2         3 my $in = shift @$toks;
1286             die "'in' pro 'per' afuit" . adversum($in)
1287 2 50       8 unless $in->{lex} eq 'IN';
1288 2         3 $self->{D} = $var;
1289 2         8 $self->{C} = conn_command($toks,'DO', 'PERIOD');
1290 2 50 33     19 unless (($self->{C}{lex}||$self->{C}{V}{lex}) =~ /DATIVE|OWNER_D/) {
1291             my ($badraw, $bad) = $self->{C}{lex}
1292             ? ($self->{C}{raw}, $self->{C})
1293 0 0       0 : ($self->{C}{V}{raw}, $self->{C}{V});
1294 0         0 die "'$badraw' dativus non est in 'per'" . adversum($bad);
1295             }
1296 2 50       5 if ($toks->[0]{lex} =~ /PERIOD|CONNECTIVE/) {
1297 0 0       0 die "Iussa absentia per '$self->{raw}'" . adversum($self)
1298             unless @$Bstack;
1299 0         0 $self->{B} = pop @$Bstack;
1300 0 0       0 shift @$toks unless $toks->[0]{lex} eq 'CONNECTIVE';
1301             }
1302             else {
1303 2         7 $self->{B} = block($toks);
1304             }
1305 2         9 return $self;
1306             }
1307              
1308             sub control
1309             {
1310 0     0 1 0 my ($toks, $Bstack) = @_;
1311 0         0 my $self = shift @$toks;
1312 0         0 $self->{C} = conn_command($toks,'DO');
1313 0 0 0     0 if (($self->{perl}||"") eq 'while' &&
      0        
      0        
1314             ($self->{C}{V}{perl}||"") eq 'Lingua::Romana::Perligata::getline') {
1315 0         0 $self->{C}{V}{diamond} = 1;
1316             }
1317 0 0 0     0 if (!@$toks || $toks->[0]{lex} =~ /PERIOD|CONNECTIVE/) {
1318 0 0       0 die "Iussa absentia per '$self->{raw}'" . adversum($self)
1319             unless @$Bstack;
1320 0         0 $self->{B} = pop @$Bstack;
1321 0 0       0 shift @$toks unless $toks->[0]{lex} eq 'CONNECTIVE';
1322             }
1323             else {
1324 0         0 $self->{B} = block($toks);
1325             }
1326 0         0 return $self;
1327             }
1328              
1329              
1330             sub __enlist__ {
1331 2     2   31 return ($_[0]..$_[1]);
1332             }
1333              
1334             sub __enquote__ {
1335 0     0   0 return join " ", @_;
1336             }
1337              
1338             sub __encatenate__ {
1339 0     0   0 return join "", @_;
1340             }
1341              
1342             sub __lastelem__(\@) {
1343 0     0   0 return $#{$_[0]};
  0         0  
1344             }
1345              
1346             my %lb = ( '@'=>'[', '%'=>'{' );
1347             my %rb = ( '@'=>']', '%'=>'}' );
1348              
1349             sub STATEMENT::translate {
1350 6     6   14 my $verb = $_[0]{V}->translate;
1351 6         9 my $prefix = $_[0]{V}{prefix};
1352 6         15 my $hasblock = $verb =~ m{^(grep|map)$};
1353             my $noparen = $_[0]{V}{lex} eq 'OWNER_D' && $_[0]{V}{raw} =~ /o$/
1354 6   33     39 || $_[0]{V}{raw} =~ /^(finis|nuntius|factor(em|i))$/
1355             || $hasblock;
1356             my $dative = defined $_[0]{D}
1357             ? $_[0]{D}->translate
1358 6 100       19 : "";
1359 6 50 33     17 my $Dref = $verb =~ /^(bless)$/ && $dative =~ /^[%@]/ ? "\\" : "";
1360 6 100       13 $dative = $Dref . $dative if $dative;
1361 6 50 33     33 my $Dcomma = $dative && defined $_[0]{A} && !$hasblock && $verb !~ /^(print|printf)$/ ? "," : "";
1362 6 50 0     30 if ($verb =~ /^(package|use)$/) {
    50          
    50          
    50          
    50          
    0          
    0          
    0          
1363 0         0 return "$verb $_[0]{A}[0]{raw} ";
1364             }
1365             elsif ($verb eq ':') { # LABEL
1366 0         0 return " $_[0]{A}[0]{raw}: ";
1367             }
1368             elsif ($_[0]{V}{diamond}) {
1369 0         0 $result = "<" . substr($dative,1) . ">";
1370             }
1371             elsif ($verb =~ /^[\$%@]\{$/) {
1372 0         0 $result = $verb . $_[0]{A}[0]->translate . '}';
1373             }
1374             elsif (! $_[0]{V}{operator}) {
1375             $result = "$verb "
1376             . ($noparen ? "" : "(")
1377             . $dative . $Dcomma
1378             . " "
1379 6 50       24 . (defined $_[0]{A} ? join ", ", map({ $_->translate($_[0]{V}) } @{$_[0]{A}}) : "")
  14 50       43  
  6 50       13  
1380             . ($noparen ? "" : ")") ;
1381             }
1382             elsif ($prefix && $dative) {
1383 0         0 $result = " $verb $dative ";
1384             }
1385             elsif ($prefix) {
1386 0         0 $result = " $verb (" . $_[0]{A}[0]->translate . ")";
1387             }
1388             elsif ( $verb eq '=' ) {
1389             $result = " "
1390             . $dative
1391             . " $verb "
1392 0         0 . ( @{$_[0]{A}} > 1
1393 0         0 ? "(" . join(",", map($_->translate, @{$_[0]{A}})) . ")"
1394 0 0       0 : $_[0]{A}[0]->translate
1395             );
1396             }
1397             else
1398             {
1399 0 0       0 my $Acount = @{$_[0]{A}||[]};
  0         0  
1400 0 0       0 my $neg = $verb =~ s/^!(<=>|cmp)/$1/ ? "!" : "";
1401             $result = " $neg("
1402             . ( $dative ? $dative
1403 0         0 : $Acount-- ? shift(@{$_[0]{A}})->translate
1404             : "")
1405             . " $verb "
1406 0 0       0 . ( $Acount ? $_[0]{A}[0]->translate : "")
    0          
    0          
1407             . ")";
1408             }
1409 6 50       15 if ($_[0]->{G}) {
1410 0         0 my $perl = pop(@{$_[0]->{G}})->{perl}; # LAST GENITIVE IS THE VARIABLE
  0         0  
1411 0         0 $perl =~ s/^([\%\@])/\$/;
1412 0         0 my $type = $1;
1413 0         0 while (my $next = pop @{$_[0]->{G}}) {
  0         0  
1414 0         0 $perl .= $lb{$type} . $next->translate . $rb{$type};
1415             }
1416 0         0 $result = $perl . $lb{$type} . $result . $rb{$type};
1417             }
1418 6   50     37 return '\\'x($_[0]{R}||0) . $result;
1419             }
1420              
1421             sub BLOCK::translate {
1422             return "{"
1423 4     4   9 . join(";\n", map {$_->translate} @{$_[0]})
  4         7  
  4         14  
1424             . "}" ;
1425             }
1426              
1427             sub CONNECTIVE::translate {
1428 0     0   0 return $_[0]{L}->translate . " $_[0]{perl} " . $_[0]{R}->translate;
1429             }
1430              
1431             sub Separator::translate {
1432 0     0   0 return $_[0]{perl};
1433             }
1434              
1435             sub SUBNAME::translate {
1436 0     0   0 return $_[0]{perl};
1437             }
1438              
1439             sub SUBNAME_OA::translate {
1440 0     0   0 return $_[0]{perl};
1441             }
1442              
1443             sub SUBNAME_A::translate {
1444 0     0   0 return $_[0]{perl};
1445             }
1446              
1447             sub SUBNAME_D::translate {
1448 0     0   0 return $_[0]{perl};
1449             }
1450              
1451             sub SUBNAME_AD::translate {
1452 2     2   5 return $_[0]{perl};
1453             }
1454              
1455             sub SUBNAME_AB::translate {
1456 0     0   0 return $_[0]{perl};
1457             }
1458              
1459             sub SUBNAME_ACCUSATIVE::translate {
1460 0     0   0 return $_[0]{perl};
1461             }
1462              
1463             sub SUBNAME_A_ACCUSATIVE::translate {
1464 0     0   0 return $_[0]{perl};
1465             }
1466              
1467             sub SUBNAME_D_ACCUSATIVE::translate {
1468 0     0   0 return $_[0]{perl};
1469             }
1470              
1471             sub SUBNAME_AD_ACCUSATIVE::translate {
1472 0     0   0 return $_[0]{perl};
1473             }
1474              
1475             sub SUBNAME_AB_ACCUSATIVE::translate {
1476 0     0   0 return $_[0]{perl};
1477             }
1478              
1479             sub SUBNAME_B_ACCUSATIVE::translate {
1480 0     0   0 return $_[0]{perl};
1481             }
1482              
1483             sub SUBNAME_DATIVE::translate {
1484 0     0   0 return $_[0]{perl};
1485             }
1486              
1487             sub SUBNAME_A_DATIVE::translate {
1488 2     2   6 return $_[0]{perl};
1489             }
1490              
1491             sub SUBNAME_D_DATIVE::translate {
1492 0     0   0 return $_[0]{perl};
1493             }
1494              
1495             sub SUBNAME_AD_DATIVE::translate {
1496 0     0   0 return $_[0]{perl};
1497             }
1498              
1499             sub SUBNAME_AB_DATIVE::translate {
1500 0     0   0 return $_[0]{perl};
1501             }
1502              
1503             sub SUBNAME_B_DATIVE::translate {
1504 0     0   0 return $_[0]{perl};
1505             }
1506              
1507             sub DATIVE::translate {
1508 2     2   4 my ($self) = @_;
1509 2         5 my $perl = $self->{perl};
1510 2 50       6 if ($self->{G}) {
1511 0         0 my $gen = pop(@{$self->{G}})->{perl}; # LAST GENITIVE IS THE VARIABLE
  0         0  
1512 0         0 $gen =~ s/^([\%\@])/\$/;
1513 0         0 my $type = $1;
1514 0         0 while (my $next = pop @{$self->{G}}) {
  0         0  
1515 0         0 $gen .= $lb{$type} . $next->translate . $rb{$type};
1516             }
1517 0         0 $perl = $gen . $lb{$type} . $perl . $rb{$type};
1518             }
1519 2         3 return $perl;
1520             }
1521              
1522             sub OWNER_D::translate {
1523 0     0   0 return $_[0]{perl};
1524             }
1525              
1526             sub ACCUSATIVE::translate {
1527 4     4   7 my ($self) = @_;
1528 4         7 my $perl = $self->{perl};
1529 4 50       7 if ($self->{G}) {
1530 0         0 my $gen = pop(@{$self->{G}})->{perl}; # LAST GENITIVE IS THE VARIABLE
  0         0  
1531 0         0 $gen =~ s/^([\%\@])/\$/;
1532 0         0 my $type = $1;
1533 0         0 while (my $next = pop @{$self->{G}}) {
  0         0  
1534 0         0 $gen .= $lb{$type} . $next->translate . $rb{$type};
1535             }
1536 0         0 $perl = $gen . $lb{$type} . $perl . $rb{$type};
1537             }
1538 4   50     23 return '\\'x($_[0]{R}||0) . $perl;
1539             }
1540              
1541             sub CONTROL::translate {
1542             return $_[0]{perl}
1543             . " (" . $_[0]{C}->translate . ") "
1544             . $_[0]{B}->translate
1545 0     0   0 . "\n";
1546             }
1547              
1548             sub FOR::translate {
1549             return $_[0]{perl}
1550             . " " . $_[0]{D}->translate
1551             . " (" . $_[0]{C}->translate . ") "
1552             . $_[0]{B}->translate
1553 2     2   10 . "\n";
1554             }
1555              
1556             sub NUMERAL::translate {
1557 4     4   7 my ($self) = @_;
1558             # if ($self->{G}) {
1559             # return $self->{G}->index($self->{perl});
1560             # }
1561 4         17 return $self->{perl};
1562             }
1563              
1564             sub ORDINAL::translate {
1565 2     2   4 my ($self) = @_;
1566 2 50       7 return $self->{perl} unless $self->{G};
1567 2         3 my $perl = pop(@{$self->{G}})->translate; # LAST GENITIVE IS THE VARIABLE
  2         6  
1568 2         15 $perl =~ s/^([\%\@])/\$/;
1569 2         5 my $type = $1;
1570 2         4 while (my $next = pop @{$self->{G}}) {
  2         7  
1571 0         0 $perl .= $lb{$type} . $next->translate . $rb{$type};
1572             }
1573 2         9 return $perl . $lb{$type} . $self->{perl} . $rb{$type};
1574             }
1575              
1576             *ORDINAL_DATIVE::translate = *ORDINAL::translate;
1577              
1578             sub DISJUNCTION::translate {
1579 0     0   0 return $_[0]{perl};
1580             }
1581              
1582             sub CONJUNCTION::translate {
1583 0     0   0 return $_[0]{perl};
1584             }
1585              
1586             sub Literal::translate {
1587 8     8   14 my ($self, $context) = @_;
1588 8         9 my $perl;
1589 8 50 66     35 if ($context && $context->{raw} =~ /^inque/) {
    50          
    50          
    100          
1590 0         0 $perl = qq{'$_[0]{raw}'}
1591             }
1592             elsif ( $self->{lex} eq 'GENITIVE' ) {
1593 0         0 $perl = $self->GENITIVE::translate;
1594             }
1595             elsif ( $self->{lex} eq 'SUBNAME_A_GENITIVE' ) {
1596 0         0 $perl = $self->SUBNAME_A_GENITIVE::translate;
1597             }
1598             elsif ( $self->{lex} eq 'INFINITIVE' ) {
1599 2         7 $perl = "sub $_[0]{perl}\n" . $_[0]{B}->translate;
1600             }
1601             else {
1602             $perl = '\\'x($_[0]{R}||0) . $_[0]{perl}
1603 6   50     26 }
1604 8 50       17 if ($self->{G}) {
1605 0         0 my $gen = pop(@{$self->{G}})->{perl}; # LAST GENITIVE IS THE VARIABLE
  0         0  
1606 0         0 $gen =~ s/^([\%\@])/\$/;
1607 0         0 my $type = $1;
1608 0         0 while (my $next = pop @{$self->{G}}) {
  0         0  
1609 0         0 $gen .= $lb{$type} . $next->translate . $rb{$type};
1610             }
1611 0         0 $perl = $gen . $lb{$type} . $perl . $rb{$type};
1612             }
1613 8         27 return $perl;
1614             }
1615              
1616             sub GENITIVE::translate {
1617 2     2   4 return $_[0]{perl};
1618             };
1619              
1620             sub SUBNAME_A_GENITIVE::translate {
1621 0     0   0 return $_[0]{perl};
1622             };
1623              
1624             sub Name::translate {
1625 2     2   8 return qq{'$_[0]{raw}'}
1626             }
1627              
1628             1;
1629              
1630             __END__