File Coverage

lib/ChordPro/Chords/Parser.pm
Criterion Covered Total %
statement 523 731 71.5
branch 158 260 60.7
condition 78 155 50.3
subroutine 79 120 65.8
pod 0 10 0.0
total 838 1276 65.6


line stmt bran cond sub pod time code
1             #! perl
2              
3 90     90   1279 use v5.26;
  90         373  
4 90     90   600 use utf8;
  90         212  
  90         583  
5 90     90   3082 use Carp;
  90         204  
  90         6505  
6 90     90   651 use feature qw( signatures );
  90         247  
  90         13534  
7 90     90   680 no warnings "experimental::signatures";
  90         242  
  90         5188  
8              
9             # package ParserWatch;
10             #
11             # require Tie::Hash;
12             # our @ISA = qw( Tie::StdHash );
13             #
14             # sub STORE {
15             # if ( $_[1] !~ /^[[:alpha:]]+$/ ) {
16             # Carp::cluck("STORE $_[1] " . $_[2]);
17             # ::dump($_[2]);
18             # }
19             # $_[0]->{$_[1]} = $_[2];
20             # }
21              
22 90     90   649 use ChordPro;
  90         218  
  90         134829  
23              
24             my %parsers;
25              
26             # tie %parsers => 'ParserWatch';
27              
28             package ChordPro::Chords::Parser;
29              
30             # The parser analyses a chord and returns an object with the following
31             # attributes:
32             #
33             # name name as passed to the parser (e.g. Cism7)
34             #
35             # root textual decomposition: root part (e.g. Cis)
36             # qual textual decomposition: quality part (e.g. m)
37             # ext textual decomposition: extension part (e.g. 7)
38             # bass textual decomposition: bass part (a root)
39             #
40             # system notation system (common, nashville, user defined)
41             # root_canon canonical root (e.g. Cis => C#)
42             # root_ord root ordinal (e.g. Cis => 1)
43             # root_mod root modifier (e.g. is => # => +1)
44             # qual_canon canonical qualifier (e.g. m => -)
45             # ext_canon canonical extension (e.g. sus => sus4)
46             # bass_canon like root, for bass note
47             # bass_ord like root, for bass note
48             # bass_mod like root, for bass note
49             #
50             # The parsers are one of
51             # ChordPro::Chords::Parser::Common
52             # ChordPro::Chords::Parser::Nashville
53             # ChordPro::Chords::Parser::Roman
54             #
55             # The objects are one of
56             # ChordPro::Chord::Common
57             # ChordPro::Chord::Nashville
58             # ChordPro::Chord::Roman
59              
60             # Creates a parser based on the current (optionally augmented)
61             # context.
62             # Note that the appropriate way is to call
63             # ChordPro::Chords::Parser->get_parser.
64              
65 2     2 0 7 sub new ( $pkg, $init ) {
  2         7  
  2         6  
  2         5  
66              
67 2 50       9 Carp::confess("Missing config?") unless $::config;
68             # Use current config, optionally augmented by $init.
69 2   50     5 my $cfg = { %{$::config//{}}, %{$init//{}} };
  2   50     27  
  2         201  
70              
71             Carp::croak("Missing notes in parser creation")
72 2 50       21 unless $cfg->{notes};
73 2         9 my $system = $cfg->{notes}->{system};
74 2 50       9 Carp::croak("Missing notes system in parser creation")
75             unless $system;
76              
77 2 50       9 if ( $system eq "nashville" ) {
78 0         0 return ChordPro::Chords::Parser::Nashville->new($cfg);
79             }
80 2 50       9 if ( $system eq "roman" ) {
81 0         0 return ChordPro::Chords::Parser::Roman->new($cfg);
82             }
83 2         23 return ChordPro::Chords::Parser::Common->new($cfg);
84             }
85              
86             # The default parser has built-in support for common (dutch) note
87             # names.
88              
89 11515     11515 0 19670 sub default ( $pkg ) {
  11515         21733  
  11515         18848  
90              
91             return $parsers{common} //=
92             ChordPro::Chords::Parser::Common->new
93 11515   33     54936 ( { %{$::config},
  0         0  
94             "notes" =>
95             { "system" => "common",
96             "sharp" => [ "C", [ "C#", "Cis", "C♯" ],
97             "D", [ "D#", "Dis", "D♯" ],
98             "E",
99             "F", [ "F#", "Fis", "F♯" ],
100             "G", [ "G#", "Gis", "G♯" ],
101             "A", [ "A#", "Ais", "A♯" ],
102             "B",
103             ],
104             "flat" => [ "C",
105             [ "Db", "Des", "D♭" ], "D",
106             [ "Eb", "Es", "Ees", "E♭" ], "E",
107             "F",
108             [ "Gb", "Ges", "G♭" ], "G",
109             [ "Ab", "As", "Aes", "A♭" ], "A",
110             [ "Bb", "Bes", "B♭" ], "B",
111             ],
112             },
113             },
114             );
115             }
116              
117             # Cached version of the individual parser's parse_chord.
118 64890     64890 0 103338 sub parse ( $self, $chord ) {
  64890         104604  
  64890         109393  
  64890         93452  
119             #### $self->{chord_cache}->{$chord} //=
120 64890         176267 $self->parse_chord($chord);
121             }
122              
123             # Virtual.
124 0     0 0 0 sub parse_chord ( $self, $chord ) {
  0         0  
  0         0  
  0         0  
125 0         0 Carp::confess("Virtual method 'parse_chord' not defined");
126             }
127              
128             # Fetch a parser for a known system, with fallback.
129             # Default is a parser for the current config.
130 583     583 0 1483 sub get_parser ( $self, $system = undef, $nofallback = undef ) {
  583         1473  
  583         1599  
  583         1390  
  583         1136  
131              
132 583   66     2910 $system //= $::config->{notes}->{system};
133 583 100       3987 return $parsers{$system} if $parsers{$system};
134              
135 311 100       2829 if ( $system eq "nashville" ) {
    100          
    50          
    50          
    0          
136 2   33     31 return $parsers{$system} //=
137             ChordPro::Chords::Parser::Nashville->new;
138             }
139             elsif ( $system eq "roman" ) {
140 2   33     42 return $parsers{$system} //=
141             ChordPro::Chords::Parser::Roman->new;
142             }
143             elsif ( $system ne $::config->{notes}->{system} ) {
144 0         0 my $p = ChordPro::Chords::Parser::Common->new
145             ( { notes => { system => $system } } );
146 0         0 return $parsers{$system} = $p;
147             }
148             elsif ( $system ) {
149 307         2517 my $p = ChordPro::Chords::Parser::Common->new;
150 307         955 $p->{system} = $system;
151 307         1814 return $parsers{$system} = $p;
152             }
153             elsif ( $nofallback ) {
154 0         0 return;
155             };
156              
157 0         0 Carp::confess("No parser for $system, falling back to default\n");
158 0   0     0 return $parsers{common} //= $self->default;
159             }
160              
161 4     4 0 13 sub have_parser ( $self, $system ) {
  4         8  
  4         37  
  4         9  
162 4         31 exists $parsers{$system};
163             }
164              
165             # The list of instantiated parsers.
166 0     0 0 0 sub parsers ( $self ) {
  0         0  
  0         0  
167 0         0 \%parsers;
168             }
169              
170 219     219 0 600 sub reset_parsers ( $self, @which ) {
  219         2142  
  219         3520  
  219         423  
171 219 50       1246 @which = keys(%parsers) unless @which;
172 219         4674 delete $parsers{$_} for @which;
173             }
174              
175             # The number of intervals for this note system.
176 379     379 0 765 sub intervals ( $self ) {
  379         738  
  379         617  
177 379         1356 $self->{intervals};
178             }
179              
180 0     0 0 0 sub simplify ( $self ) {
  0         0  
  0         0  
181 0         0 ref($self);
182             }
183              
184             ################ Parsing Common notated chords ################
185              
186             package ChordPro::Chords::Parser::Common;
187              
188             our @ISA = qw( ChordPro::Chords::Parser );
189              
190 90     90   919 use Storable qw(dclone);
  90         191  
  90         346401  
191              
192 309     309   698 sub new ( $pkg, $cfg = $::config ) {
  309         642  
  309         745  
  309         582  
193 309         1626 my $self = bless { chord_cache => {} } => $pkg;
194 309         940 bless $self => 'ChordPro::Chords::Parser::Common';
195 309         857 my $notes = $cfg->{notes};
196 309         1756 $self->load_notes($cfg);
197 309         1279 $self->{system} = $notes->{system};
198 309         1178 $self->{target} = 'ChordPro::Chord::Common';
199 309         1103 $self->{movable} = $notes->{movable};
200             warn("Chords: Created parser for ", $self->{system},
201             $cfg->{settings}->{chordnames} eq "relaxed"
202             ? ", relaxed" : "",
203 309 0       1725 "\n") if $::options->{verbose} > 1;
    50          
204 309         1673 return $parsers{$self->{system}} = $self;
205             }
206              
207 63870     63870   103726 sub parse_chord ( $self, $chord ) {
  63870         100093  
  63870         115186  
  63870         95754  
208              
209             my $info = { system => $self->{system},
210 63870         320858 parser => $self,
211             name => $chord };
212              
213 63870         135539 my $bass = "";
214 63870 100       549383 if ( $chord =~ m;^(.*)/($self->{n_pat})$; ) {
215 149         571 $chord = $1;
216 149         445 $bass = $2;
217             }
218              
219 63870         125798 my %plus;
220              
221             # Match chord.
222 63870 50 33     1331059 if ( $chord eq "" && $bass ne "" ) {
    100 66        
    100 100        
    100 66        
223 0         0 $info->{rootless} = 1;
224             }
225             elsif ( $chord =~ /^$self->{c_pat}$/ ) {
226 39891         735076 %plus = %+;
227 39891         192583 $info->{root} = $plus{root};
228             }
229             # Retry with relaxed pattern if requested.
230             elsif ( $self->{c_rpat}
231             && $::config->{settings}->{chordnames} eq "relaxed"
232             && $chord =~ /^$self->{c_rpat}$/ ) {
233 22         461 %plus = %+; # keep it outer
234 22 50       140 return unless $info->{root} = $plus{root};
235             }
236             # Not a chord. Try note name.
237             elsif ( $::config->{settings}->{notenames}
238             && ucfirst($chord) =~ /^$self->{n_pat}$/ ) {
239 6         30 $info->{root} = $chord;
240 6         19 $info->{isnote} = 1;
241             }
242             # Nope.
243             else {
244 23951         184667 return;
245             }
246              
247 39919         124735 bless $info => $self->{target};
248              
249 39919   100     125059 my $q = $plus{qual} // "";
250 39919         88779 $info->{qual} = $q;
251 39919 100 66     220894 $q = "-" if $q eq "m" || $q eq "mi" || $q eq "min";
      100        
252 39919 100       99887 $q = "+" if $q eq "aug";
253 39919 100       88063 $q = "0" if $q eq "dim";
254 39919 100       93669 $q = "0" if $q eq "o";
255 39919         103016 $info->{qual_canon} = $q;
256              
257 39919   100     110672 my $x = $plus{ext} // "";
258 39919 100       111534 if ( !$info->{qual} ) {
259 19796 100       50296 if ( $x eq "maj" ) {
260 48         136 $x = "";
261             }
262             }
263 39919         103014 $info->{ext} = $x;
264 39919 100       91873 $x = "sus4" if $x eq "sus";
265 39919         85076 $info->{ext_canon} = $x;
266              
267             my $ordmod = sub {
268 40068     40068   90963 my ( $pfx ) = @_;
269 40068         88503 my $r = $info->{$pfx};
270 40068 100       110887 $r = ucfirst($r) if $info->{isnote};
271 40068 100       150865 if ( defined $self->{ns_tbl}->{$r} ) {
    50          
272 29251         95016 $info->{"${pfx}_ord"} = $self->{ns_tbl}->{$r};
273 29251 100       105085 $info->{"${pfx}_mod"} = defined $self->{nf_tbl}->{$r} ? 0 : 1;
274 29251         129027 $info->{"${pfx}_canon"} = $self->{ns_canon}->[$self->{ns_tbl}->{$r}];
275             }
276             elsif ( defined $self->{nf_tbl}->{$r} ) {
277 10817         37022 $info->{"${pfx}_ord"} = $self->{nf_tbl}->{$r};
278 10817         26273 $info->{"${pfx}_mod"} = -1;
279 10817         48538 $info->{"${pfx}_canon"} = $self->{nf_canon}->[$self->{nf_tbl}->{$r}];
280             }
281             else {
282 0         0 Carp::croak("CANT HAPPEN ($r)");
283 0         0 return;
284             }
285             #### $info->{isflat} = $info->{"${pfx}_mod"} < 0;
286 39919         292747 };
287              
288 39919 50       138561 $ordmod->("root") unless $info->is_rootless;
289              
290 39919 50       168552 cluck("BLESS info for $chord into ", $self->{target}, "\n")
291             unless ref($info) =~ /ChordPro::Chord::/;
292              
293 39919 100       149938 if ( $info->{bass} = $bass ) {
294 149 50       3969 if ( $bass =~ /^$self->{n_pat}$/ ) {
295 149         551 $ordmod->("bass");
296 149 50       516 if ( $info->is_rootless ) {
297 0         0 for ( qw( ord mod canon ) ) {
298 0         0 $info->{"root_$_"} = $info->{"bass_$_"};
299             }
300             }
301             }
302             }
303              
304 39919 50       222601 if ( $::config->{settings}->{'chords-canonical'} ) {
305 0         0 my $t = $info->{name};
306 0         0 $info->{name_canon} = $info->canonical;
307             warn("Parsing chord: \"$chord\" canon \"", $info->canonical, "\"\n" )
308 0 0 0     0 if $info->{name_canon} ne $t and $::config->{debug}->{chords};
309             }
310              
311 39919         521752 return $info;
312             }
313              
314             ################ Chords ################
315              
316             # The following additions are recognized for major chords.
317              
318             my $additions_maj =
319             {
320             map { $_ => $_ }
321             "",
322             "11",
323             "13",
324             "13#11",
325             "13#9",
326             "13b9",
327             "2",
328             "3",
329             "4",
330             "5",
331             "6",
332             "69",
333             "6add9",
334             "7",
335             "711",
336             "7add11",
337             "7#11",
338             "7#5",
339             "7#9",
340             "7#9#11",
341             "7#9#5",
342             "7#9b5",
343             "7alt",
344             "7b13",
345             "7b13sus",
346             "7b5",
347             "7b9",
348             "7b9#11",
349             "7b9#5",
350             "7b9#9",
351             "7b9b13",
352             "7b9b5",
353             "7b9sus",
354             "7-13",
355             "7-13sus",
356             "7-5",
357             "7\\+5",
358             "7-9",
359             "7-9#11",
360             "7-9#5",
361             "7-9#9",
362             "7-9-13",
363             "7-9-5",
364             "7-9sus",
365             "7sus",
366             "7susadd3",
367             "7\\+", # REGEXP!!!
368             "9",
369             "9\\+", # REGEXP!!!
370             "911",
371             "9#11",
372             "9#5",
373             "9b5",
374             "9-5",
375             "9sus",
376             "9add6",
377             ( map { ( "maj$_", "^$_" ) }
378             "",
379             "13",
380             "7",
381             "711",
382             "7#11",
383             "7#5",
384             ( map { "7sus$_" } "", "2", "4" ),
385             "9",
386             "911",
387             "9#11",
388             ),
389             "alt",
390             "h",
391             "h7",
392             "h9",
393             ( map { "add$_" } "2", "4", "9", "11" ),
394             ( map { "sus$_" } "", "2", "4", "9" ),
395             ( map { "6sus$_" } "", "2", "4" ),
396             ( map { "7sus$_" } "", "2", "4" ),
397             ( map { "13sus$_" } "", "2", "4" ),
398             };
399              
400             # The following additions are recognized for minor chords.
401              
402             my $additions_min =
403             {
404             map { $_ => $_ }
405             "",
406             "#5",
407             "11",
408             "13",
409             "6",
410             "69",
411             "7b5",
412             "7-5",
413             "711", # for George Kooymans
414             ( map { ( "$_", "maj$_", "^$_" ) }
415             "7",
416             "9",
417             ),
418             "9maj7", "9^7",
419             "add9",
420             "b6",
421             "#7",
422             ( map { "sus$_" } "", "4", "9" ),
423             ( map { "7sus$_" } "", "4" ),
424             };
425              
426             # The following additions are recognized for augmented chords.
427              
428             my $additions_aug =
429             {
430             map { $_ => $_ }
431             "",
432             "7",
433             };
434              
435             # The following additions are recognized for diminished chords.
436              
437             my $additions_dim =
438             {
439             map { $_ => $_ }
440             "",
441             "7",
442             };
443              
444             # Build tables and patterns from the "notes" element from the
445             # configuration.
446              
447 309     309   728 sub load_notes ( $self, $init ) {
  309         703  
  309         647  
  309         643  
448 309   50     683 my $cfg = { %{$::config//{}}, %{$init//{}} };
  309   50     3376  
  309         7463  
449 309         2010 my $n = $cfg->{notes};
450 309 50       1256 Carp::confess("No notes?") unless $n->{system};
451 309         1649 my ( @ns_canon, %ns_tbl, @nf_canon, %nf_tbl );
452              
453 309         750 my $rix = 0;
454 309         690 foreach my $root ( @{ $n->{sharp} } ) {
  309         1246  
455 3708 100       12356 if ( UNIVERSAL::isa($root, 'ARRAY') ) {
456 1530         3844 $ns_canon[$rix] = $root->[0];
457 1530         8500 $ns_tbl{$_} = $rix foreach @$root;
458             }
459             else {
460 2178         4742 $ns_canon[$rix] = $root;
461 2178         5211 $ns_tbl{$root} = $rix;
462             }
463 3708         6168 $rix++;
464             }
465 309         843 $rix = 0;
466 309         713 foreach my $root ( @{ $n->{flat} } ) {
  309         1136  
467 3708 100       9621 if ( UNIVERSAL::isa($root, 'ARRAY') ) {
468 1539         3587 $nf_canon[$rix] = $root->[0];
469 1539         8222 $nf_tbl{$_} = $rix foreach @$root;
470             }
471             else {
472 2169         4389 $nf_canon[$rix] = $root;
473 2169         4517 $nf_tbl{$root} = $rix;
474             }
475 3708         5994 $rix++;
476             }
477              
478             # Pattern to match note names.
479 309         882 my $n_pat = '(?:' ;
480 309         719 my @n;
481 309         2049 foreach ( keys %ns_tbl ) {
482 6743         11462 push( @n, $_ );
483             }
484 309         5235 foreach ( sort keys %nf_tbl ) {
485 7354 100       14894 next if $ns_tbl{$_};
486 5500         9603 push( @n, $_ );
487             }
488              
489 309         2727 $n_pat = '(?:' . join( '|', sort { length($b) <=> length($a) } @n ) . ')';
  49266         76766  
490              
491             # Pattern to match chord names.
492 309         794 my $c_pat;
493             # Accept root, qual, and only known extensions.
494 309         877 $c_pat = "(?" . $n_pat . ")";
495 309         1001 $c_pat .= "(?:";
496 309         4570 $c_pat .= "(?-|min?|m(?!aj))".
497             "(?" . join("|", keys(%$additions_min)) . ")|";
498 309         2048 $c_pat .= "(?\\+|aug)".
499             "(?" . join("|", keys(%$additions_aug)) . ")|";
500 309         1358 $c_pat .= "(?0|o|dim|h)".
501             "(?" . join("|", keys(%$additions_dim)) . ")|";
502 309         11157 $c_pat .= "(?)".
503             "(?" . join("|", keys(%$additions_maj)) . ")";
504 309         2282 $c_pat .= ")";
505 309         158861 $c_pat = qr/$c_pat/;
506 309         42136 $n_pat = qr/$n_pat/;
507              
508             # In relaxed form, we accept anything for extension.
509 309         3038 my $c_rpat = "(?" . $n_pat . ")";
510 309         1101 $c_rpat .= "(?:(?-|min?|m(?!aj)|\\+|aug|0|o|dim|)(?.*))";
511 309         50895 $c_rpat = qr/$c_rpat/;
512              
513             # Store in the object.
514 309         3524 $self->{n_pat} = $n_pat;
515 309         980 $self->{c_pat} = $c_pat;
516 309         950 $self->{c_rpat} = $c_rpat;
517 309         1191 $self->{ns_tbl} = \%ns_tbl;
518 309         1132 $self->{nf_tbl} = \%nf_tbl;
519 309         1199 $self->{ns_canon} = \@ns_canon;
520 309         978 $self->{nf_canon} = \@nf_canon;
521 309         5726 $self->{intervals} = @ns_canon;
522             }
523              
524 890     890   1366 sub root_canon ( $self, $root, $sharp = 0, $minor = 0 ) {
  890         1554  
  890         1396  
  890         1630  
  890         1481  
  890         1273  
525 890 100       4874 ( $sharp ? $self->{ns_canon} : $self->{nf_canon} )->[$root];
526             }
527              
528             # Has chord diagrams.
529 197     197   380 sub has_diagrams ( $self ) { !$self->{movable} }
  197         802  
  197         3712  
  197         1119  
530              
531             # Movable notes system.
532 8     8   16 sub movable ( $self ) { $self->{movable} }
  8         15  
  8         13  
  8         43  
533              
534             ################ Parsing Nashville notated chords ################
535              
536             package ChordPro::Chords::Parser::Nashville;
537              
538             our @ISA = qw(ChordPro::Chords::Parser::Common);
539              
540 90     90   945 use Storable qw(dclone);
  90         190  
  90         120914  
541              
542             sub new {
543 2     2   8 my ( $pkg, $init ) = @_;
544 2         11 my $self = bless { chord_cache => {} } => $pkg;
545 2         25 $self->{system} = "nashville";
546 2         8 $self->{target} = 'ChordPro::Chord::Nashville';
547 2         8 $self->{intervals} = 12;
548             warn("Chords: Created parser for ", $self->{system}, "\n")
549 2 50 33     15 if $::options->{verbose} && $::options->{verbose} > 1;
550 2         19 return $parsers{$self->{system}} = $self;
551             }
552              
553             my $n_pat = qr/(?[b#]?)(?[1-7])/;
554              
555             my %nmap = ( 1 => 0, 2 => 2, 3 => 4, 4 => 5, 5 => 7, 6 => 9, 7 => 11 );
556             my @nmap = ( 1, 1, 2, 2, 3, 4, 4, 5, 5, 6, 6, 7, 1 );
557              
558             sub parse_chord {
559 408 50   408   2417 Carp::confess("NMC") unless UNIVERSAL::isa($_[0],__PACKAGE__);
560 408         1024 my ( $self, $chord ) = @_;
561              
562 408         1240 $chord =~ tr/\x{266d}\x{266f}\x{0394}\x{f8}\x{b0}/b#^h0/;
563              
564 408         767 my $bass = "";
565 408 50       1717 if ( $chord =~ m;^(.*)/(.*); ) {
566 0         0 $chord = $1;
567 0         0 $bass = $2;
568             }
569              
570 408 50       6326 return unless $chord =~ /^$n_pat(?-|\+|0|o|aug|m(?!aj)|dim)?(?.*)$/;
571              
572             my $info = { system => "nashville",
573             parser => $self,
574             name => $_[1],
575             root => $+{root},
576 408         5356 };
577 408         2073 bless $info => $self->{target};
578              
579 408   100     2315 my $q = $+{qual} // "";
580 408         1205 $info->{qual} = $q;
581 408 50       1218 $q = "-" if $q eq "m";
582 408 50       944 $q = "+" if $q eq "aug";
583 408 50       995 $q = "0" if $q eq "dim";
584 408 50       1081 $q = "0" if $q eq "o";
585 408         1054 $info->{qual_canon} = $q;
586              
587 408   50     1978 my $x = $+{ext} // "";
588 408         1560 $info->{ext} = $x;
589 408 50       1218 $x = "sus4" if $x eq "sus";
590 408         955 $info->{ext_canon} = $x;
591              
592             my $ordmod = sub {
593 408     408   925 my ( $pfx ) = @_;
594 408         1117 my $r = 0 + $info->{$pfx};
595 408         2042 $info->{"${pfx}_ord"} = $nmap{$r};
596 408 100       3203 if ( $+{shift} eq "#" ) {
    100          
597 120         342 $info->{"${pfx}_mod"} = 1;
598 120         303 $info->{"${pfx}_ord"}++;
599             $info->{"${pfx}_ord"} = 0
600 120 50       426 if $info->{"${pfx}_ord"} >= 12;
601             }
602             elsif ( $+{shift} eq "b" ) {
603 120         382 $info->{"${pfx}_mod"} = -1;
604 120         254 $info->{"${pfx}_ord"}--;
605             $info->{"${pfx}_ord"} += 12
606 120 50       497 if $info->{"${pfx}_ord"} < 0;
607             }
608             else {
609 168         536 $info->{"${pfx}_mod"} = 0;
610             }
611 408         1600 $info->{"${pfx}_canon"} = $r;
612 408         2881 };
613              
614 408         2580 $ordmod->("root");
615              
616 408         1096 $info->{bass} = $bass;
617 408 50       5568 return $info unless $bass;
618 0 0       0 return unless $bass =~ /^$n_pat$/;
619 0         0 $ordmod->("bass");
620              
621 0         0 return $info;
622             }
623              
624 0     0   0 sub load_notes { Carp::confess("OOPS") }
625              
626             sub root_canon {
627 12 50   12   61 Carp::confess("NMC") unless UNIVERSAL::isa($_[0],__PACKAGE__);
628 12         37 my ( $self, $root, $sharp ) = @_;
629 90     90   849 no warnings 'qw';
  90         217  
  90         25014  
630 12 50       64 $sharp
631             ? qw( 1 #1 2 #2 3 4 #4 5 #5 6 #6 7 )[$root]
632             : qw( 1 b2 2 b3 3 4 b5 5 b6 6 b7 7 )[$root]
633             }
634              
635             # Has chord diagrams.
636             sub has_diagrams {
637 2 50   2   13 Carp::confess("NMC") unless UNIVERSAL::isa($_[0],__PACKAGE__);
638 2         9 0;
639             }
640              
641             # Movable notes system.
642             sub movable {
643 2 50   2   13 Carp::confess("NMC") unless UNIVERSAL::isa($_[0],__PACKAGE__);
644 2         10 1;
645             }
646              
647             ################ Parsing Roman notated chords ################
648              
649             package ChordPro::Chords::Parser::Roman;
650              
651 90     90   743 use ChordPro;
  90         203  
  90         129017  
652              
653             our @ISA = qw(ChordPro::Chords::Parser::Common);
654              
655             sub new {
656 2     2   10 my ( $pkg, $init ) = @_;
657 2         10 my $self = bless { chord_cache => {} } => $pkg;
658 2         28 $self->{system} = "roman";
659 2         9 $self->{target} = 'ChordPro::Chord::Roman';
660 2         8 $self->{intervals} = 12;
661             warn("Chords: Created parser for ", $self->{system}, "\n")
662 2 50 33     14 if $::options->{verbose} && $::options->{verbose} > 1;
663 2         17 return $parsers{$self->{system}} = $self;
664             }
665              
666             my $r_pat = qr/(?[b#]?)(?(?i)iii|ii|iv|i|viii|vii|vi|v)/;
667              
668             my %rmap = ( I => 0, II => 2, III => 4, IV => 5, V => 7, VI => 9, VII => 11 );
669              
670             sub parse_chord {
671 612 50   612   4483 Carp::confess("NMC") unless UNIVERSAL::isa($_[0],__PACKAGE__);
672 612         2045 my ( $self, $chord ) = @_;
673              
674 612         2268 $chord =~ tr/\x{266d}\x{266f}\x{0394}\x{f8}\x{b0}/b#^h0/;
675              
676 612         1583 my $bass = "";
677 612 50       3928 if ( $chord =~ m;^(.*)/(.*); ) {
678 0         0 $chord = $1;
679 0         0 $bass = $2;
680             }
681              
682 612 50       16659 return unless $chord =~ /^$r_pat(?\+|0|o|aug|dim|h)?(?.*)$/;
683 612         9291 my $r = $+{shift}.$+{root};
684              
685 612         5337 my $info = { system => "roman",
686             parser => $self,
687             name => $_[1],
688             root => $r };
689 612         2768 bless $info => $self->{target};
690              
691 612   100     5786 my $q = $+{qual} // "";
692 612         2321 $info->{qual} = $q;
693 612 100       3086 $q = "-" if $r eq lc($r);
694 612 50       1853 $q = "+" if $q eq "aug";
695 612 50       1553 $q = "0" if $q eq "dim";
696 612 50       1588 $q = "0" if $q eq "o";
697 612         2530 $info->{qual_canon} = $q;
698              
699 612   50     3815 my $x = $+{ext} // "";
700 612         1972 $info->{ext} = $x;
701 612 50       1982 $x = "sus4" if $x eq "sus";
702 612 50       2101 $x = "^7" if $x eq "7+";
703 612         1881 $info->{ext_canon} = $x;
704              
705             my $ordmod = sub {
706 612     612   2062 my ( $pfx ) = @_;
707 612         1763 my $r = $info->{$pfx};
708 612         3800 $info->{"${pfx}_ord"} = $rmap{uc $r};
709 612 100       5309 if ( $+{shift} eq "#" ) {
    100          
710 180         860 $info->{"${pfx}_mod"} = 1;
711 180         648 $info->{"${pfx}_ord"}++;
712             $info->{"${pfx}_ord"} = 0
713 180 50       1101 if $info->{"${pfx}_ord"} >= 12;
714             }
715             elsif ( $+{shift} eq "b" ) {
716 180         769 $info->{"${pfx}_mod"} = -1;
717 180         748 $info->{"${pfx}_ord"}--;
718             $info->{"${pfx}_ord"} += 12
719 180 50       920 if $info->{"${pfx}_ord"} < 0;
720             }
721             else {
722 252         844 $info->{"${pfx}_mod"} = 0;
723             }
724 612         4513 $info->{"${pfx}_canon"} = $r;
725 612         5049 };
726              
727 612         2744 $ordmod->("root");
728              
729 612         1986 $info->{bass} = uc $bass;
730 612 50       10117 return $info unless $bass;
731 0 0       0 return unless $bass =~ /^$r_pat$/;
732 0         0 $ordmod->("bass");
733              
734 0         0 return $info;
735             }
736              
737 0     0   0 sub load_notes { Carp::confess("OOPS") }
738              
739             sub root_canon {
740 12 50   12   59 Carp::confess("NMC") unless UNIVERSAL::isa($_[0],__PACKAGE__);
741 12         34 my ( $self, $root, $sharp, $minor ) = @_;
742 12 50       37 return lc( $self->root_canon( $root, $sharp ) ) if $minor;
743 90     90   819 no warnings 'qw';
  90         214  
  90         26795  
744 12 50       69 $sharp
745             ? qw( I #I II #II III IV #IV V #V VI #VI VII )[$root]
746             : qw( I bII II bIII III IV bV V bVI VI bVII VII )[$root]
747             }
748              
749             # Has chord diagrams.
750             sub has_diagrams {
751 2 50   2   15 Carp::confess("NMC") unless UNIVERSAL::isa($_[0],__PACKAGE__);
752 2         9 0;
753             }
754              
755             # Movable notes system.
756             sub movable {
757 2 50   2   14 Carp::confess("NMC") unless UNIVERSAL::isa($_[0],__PACKAGE__);
758 2         11 1;
759             }
760              
761             ################ Chord objects: Common ################
762              
763             package ChordPro::Chord::Base;
764              
765 90     90   775 use Storable qw(dclone);
  90         204  
  90         163581  
766              
767             sub new {
768 13585     13585   36940 my ( $pkg, $data ) = @_;
769 13585   66     47553 $pkg = ref($pkg) || $pkg;
770 13585         110381 bless { %$data } => $pkg;
771             }
772              
773             sub clone {
774 381 50   381   1942 Carp::confess("NMC") unless UNIVERSAL::isa($_[0],__PACKAGE__);
775 381         899 my ( $self ) = shift;
776 381         56468 dclone($self);
777             }
778              
779 40881     40881   139518 sub is_note { $_[0]->{isnote} };
780 0     0   0 sub is_flat { $_[0]->{isflat} };
781              
782             sub is_nc {
783 1854     1854   3842 my ( $self ) = @_;
784             # Keyboard...
785 1854 100 66     4861 return 1 if defined($self->kbkeys) && !@{$self->kbkeys};
  1854         3776  
786             # Strings...
787 1846 100 100     3185 return unless @{ $self->frets // [] };
  1846         4779  
788 1636         2656 for ( @{ $self->frets } ) {
  1636         3211  
789 3370 100       11509 return unless $_ < 0;
790             }
791 0         0 return 1; # all -1 => N.C.
792             }
793              
794             # Can be transposed/transcoded.
795             sub is_xpxc {
796 1383 50 33 1383   7392 defined($_[0]->{root}) || defined($_[0]->{bass}) || $_[0]->is_nc;
797             }
798              
799             sub has_diagram {
800 814     814   1612 my ( $self ) = @_;
801             ( $::config->{instrument}->{type} eq "keyboard" )
802 0   0     0 ? @{ $self->kbkeys // []}
803 814 50 50     2063 : @{ $self->frets // []};
  814         1897  
804             }
805              
806             # For convenience.
807 170283     170283   681204 sub is_chord { defined $_[0]->{root_ord} }
808 79188     79188   327191 sub is_rootless { $_[0]->{rootless} }
809 588     588   2286 sub is_annotation { 0 }
810 0     0   0 sub is_movable { $_[0]->{movable} }
811 0     0   0 sub is_gridstrum { 0 }
812              
813             # Common accessors.
814             sub name {
815 6657     6657   13741 my ( $self, $np ) = @_;
816             Carp::confess("Double parens")
817 6657 50 33     17614 if $self->{parens} && $self->{name} =~ /^\(.*\)$/;
818 6657 50 33     58688 return $self->{name} if $np || !$self->{parens};
819 0         0 "(" . $self->{name} . ")";
820             }
821              
822 0     0   0 sub canon { $_[0]->{name_canon} }
823 4     4   36 sub root { $_[0]->{root} }
824 4     4   34 sub qual { $_[0]->{qual} }
825 4     4   39 sub ext { $_[0]->{ext} }
826 4     4   34 sub bass { $_[0]->{bass} }
827 3     3   26 sub base { $_[0]->{base} }
828 4297     4297   16441 sub frets { $_[0]->{frets} }
829 1     1   14 sub fingers { $_[0]->{fingers} }
830 0     0   0 sub display { $_[0]->{display} }
831 0     0   0 sub format { $_[0]->{format} }
832 7     7   79 sub diagram { $_[0]->{diagram} }
833 71     71   259 sub parser { $_[0]->{parser} }
834              
835             sub strings {
836 0     0   0 $_[0]->{parser}->{intervals};
837             }
838              
839             sub kbkeys {
840 3709 100 100 3709   9577 return $_[0]->{keys} if $_[0]->{keys} && @{$_[0]->{keys}};
  3467         15445  
841 538         2286 $_[0]->{keys} = ChordPro::Chords::get_keys($_[0]);
842             }
843              
844 775     775   1275 sub chord_display ( $self, $default ) {
  775         1263  
  775         1517  
  775         1193  
845              
846 90     90   65894 use String::Interpolate::Named;
  90         262278  
  90         69839  
847              
848 775         2532 my $res = $self->name;
849 775         1678 my $args = {};
850 775   33     4909 $self->flat_copy( $args, $self->{display} // $self );
851              
852 775 0 33     5092 if ( !$::config->{settings}->{'enharmonic-transpose'} && $args->{key} ) {
853             $args->{root} = 'E#'
854 0 0 0     0 if $args->{root} eq 'F' && $args->{key} eq 'F#';
855             $args->{root} = 'Cb'
856 0 0 0     0 if $args->{root} eq 'B' && $args->{key} eq 'Gb';
857             }
858              
859 775         2870 for my $fmt ( $default,
860             $self->{format},
861             $self->{chordformat} ) {
862 2325 100       614165 next unless $fmt;
863 789 100       2746 $args->{root} = lc($args->{root}) if $self->is_note;
864 789         3584 $args->{formatted} = $res;
865 789         4064 $res = interpolate( { args => $args }, $fmt );
866             }
867              
868             # Substitute musical symbols if wanted.
869             $res = $self->fix_musicsyms($res)
870 775 100 66     7522 if $::config->{settings}->{truesf} || $::config->{settings}->{maj7delta};
871 775         10785 return $res;
872             }
873              
874 862     862   1410 sub flat_copy ( $self, $ret, $o, $pfx = "" ) {
  862         1325  
  862         1315  
  862         1235  
  862         1669  
  862         1180  
875 862         4193 while ( my ( $k, $v ) = each %$o ) {
876 17566 100 100     62745 if ( $k eq "orig" || $k eq "xc" || $k eq "xp" ) {
      100        
877 87         426 $self->flat_copy( $ret, $v, "$k.$pfx");
878 87         283 $ret->{"$k.${pfx}formatted"} = $v->chord_display;
879             }
880             else {
881 17479         67611 $ret->{"$pfx$k"} = $v;
882             }
883             }
884 862         1941 $ret;
885             }
886              
887 22     22   44 sub fix_musicsyms ( $self, $str ) {
  22         45  
  22         45  
  22         30  
888              
889 90     90   937 use ChordPro::Utils qw( splitmarkup );
  90         194  
  90         316927  
890              
891 22         53 my $sf = $::config->{settings}->{truesf};
892 22         55 my $delta = $::config->{settings}->{maj7delta};
893              
894 22         125 my @c = splitmarkup($str);
895 22         54 my $res = '';
896 22 100       114 push( @c, '' ) if @c % 2;
897 22         45 my $did = 0; # TODO: not for roman
898 22         59 while ( @c ) {
899 37         87 for ( shift(@c) ) {
900 37 50       84 if ( $sf ) {
901 37 100       113 if ( $did ) {
902 15         51 s/b/♭/g;
903             }
904             else {
905 22         132 s/(?<=[[:alnum:]])b/♭/g;
906 22         40 $did++;
907             }
908 37         113 s/#/♯/g;
909             }
910 37 50       155 if ( $delta ) {
911 0         0 s/maj7/Δ/g;
912             }
913 37         157 $res .= $_ . shift(@c);
914             }
915             }
916 22         74 $res;
917             }
918              
919 0     0   0 sub simplify ( $self ) {
  0         0  
  0         0  
920 0         0 my $c = {};
921 0         0 for ( keys %$self ) {
922 0 0       0 next unless defined $self->{$_};
923 0 0       0 next if defined $c->{$_};
924 0 0 0     0 if ( UNIVERSAL::can( $self->{$_}, "simplify" ) ) {
    0          
925 0         0 $c->{$_} = $self->{$_}->simplify;
926             }
927 0         0 elsif ( ref($self->{$_}) eq 'ARRAY' && @{$self->{$_}} ) {
928 0         0 $c->{$_} = "[ " . join(" ", @{$self->{$_}}) . " ]";
  0         0  
929             }
930             else {
931 0         0 $c->{$_} = $self->{$_};
932             }
933             }
934 0         0 $c;
935             }
936              
937 0     0   0 sub dump ( $self ) {
  0         0  
  0         0  
938 0         0 ::dump($self->simplify);
939             }
940              
941             package ChordPro::Chord::Common;
942              
943             our @ISA = qw( ChordPro::Chord::Base );
944              
945             # Show reconstructs the chord from its constituents.
946             # Result is canonical.
947             sub show {
948 0     0   0 Carp::croak("call canonical instead of show");
949             }
950              
951 519     519   860 sub canonical ( $self ) {
  519         838  
  519         826  
952 519         864 my $res;
953              
954             $res =
955             $self->is_rootless
956             ? ""
957             : $self->is_chord
958             ? $self->{parser}->root_canon( $self->{root_ord},
959             $self->{root_mod} >= 0,
960             $self->{qual} eq '-',
961             # !$self->is_flat ???
962             ) . $self->{qual} . $self->{ext}
963 519 50       1565 : $self->{name};
    50          
964              
965 519 100       1738 if ( $self->is_note ) {
966 4         21 return lcfirst($res);
967             }
968 515 100 66     1684 if ( $self->{bass} && $self->{bass} ne "" ) {
969             $res .= "/" .
970 3 50       14 ($self->{system} eq "roman" ? lc($self->{bass}) : $self->{bass});
971             }
972 515         1862 return $res;
973             }
974              
975             # Returns a representation indepent of notation system.
976 38601     38601   61073 sub agnostic ( $self ) {
  38601         64649  
  38601         57821  
977 38601 100 66     96343 return if $self->is_rootless || $self->is_note;
978             join( " ", "",
979             $self->{root_ord},
980             $self->{root_mod},
981             $self->{qual_canon},
982             $self->{ext_canon},
983 38595   100     319580 $self->{bass_ord} // () );
984             }
985              
986 385     385   805 sub transpose ( $self, $xpose, $dir = 0 ) {
  385         730  
  385         770  
  385         721  
  385         603  
987 385 100       1080 return $self unless $xpose;
988 372 50       1142 return $self unless $self->is_chord;
989 372   33     1001 $dir //= $xpose <=> 0;
990              
991 372         1185 my $info = $self->clone;
992 372         475171 my $p = $self->{parser};
993              
994 375     375   666 my $dodir = sub( $root, $dir ) {
  375         732  
  375         627  
  375         593  
995 375 100       2768 return 0 if $root =~ /^(0|2|4|5|7|9|11)$/;
996 161         745 $dir;
997 372         2031 };
998              
999 372 50       1659 unless ( $self->{rootless} ) {
1000 372         1945 $info->{root_ord} = ( $self->{root_ord} + $xpose ) % $p->intervals;
1001             $info->{root_canon} = $info->{root} =
1002             $p->root_canon( $info->{root_ord},
1003             $dir > 0,
1004 372         1717 $info->{qual_canon} eq "-" );
1005             }
1006 372 50 66     1576 if ( $self->{bass} && $self->{bass} ne "" && $self->{bass} !~ /^\d+$/ ) {
      66        
1007 3         13 $info->{bass_ord} = ( $self->{bass_ord} + $xpose ) % $p->intervals;
1008             $info->{bass_canon} = $info->{bass} =
1009 3         13 $p->root_canon( $info->{bass_ord}, $xpose > 0 );
1010 3         12 $info->{bass_mod} = $dodir->( $info->{bass_ord}, $dir );
1011             }
1012 372         1074 $info->{root_mod} = $dodir->( $info->{root_ord}, $dir );
1013 372         1308 $info->{name} = $info->{name_canon} = $info->canonical;
1014              
1015 372         2379 delete $info->{$_} for qw( copy base frets fingers keys display );
1016              
1017 372         2698 return $info;
1018             }
1019              
1020 143     143   290 sub transcode ( $self, $xcode, $key_ord = 0 ) {
  143         295  
  143         304  
  143         267  
  143         249  
1021 143 100       803 return $self unless $xcode;
1022 20 50       71 return $self unless $self->is_chord;
1023 20 50       85 return $self if $self->{system} eq $xcode;
1024 20         2868 my $info = $self->dclone;
1025             #warn("_>_XCODE = $xcode, _SELF = $self->{system}, CHORD = $info->{name}");
1026 20         24994 $info->{system} = $xcode;
1027 20         131 my $p = $self->{parser}->get_parser($xcode);
1028 20 50       96 die("OOPS ", $p->{system}, " $xcode") unless $p->{system} eq $xcode;
1029 20         794 $info->{parser} = $p;
1030 20 100 100     151 if ( $key_ord && $p->movable ) {
1031 4         31 $info->{root_ord} -= $key_ord % $p->intervals;
1032             }
1033             # $info->{$_} = $p->{$_} for qw( ns_tbl nf_tbl ns_canon nf_canon );
1034 20 50       83 unless ( $self->{rootless} ) {
1035             $info->{root_canon} = $info->{root} =
1036             $p->root_canon( $info->{root_ord},
1037             $info->{root_mod} >= 0,
1038 20         137 $info->{qual_canon} eq "-" );
1039             }
1040 20 50 66     114 if ( $p->{system} eq "roman" && $info->{qual_canon} eq "-" ) {
1041             # Minor quality is in the root name.
1042 0         0 $info->{qual_canon} = $info->{qual} = "";
1043             }
1044 20 50 33     80 if ( $self->{bass} && $self->{bass} ne "" ) {
1045 0 0 0     0 if ( $key_ord && $p->movable ) {
1046 0         0 $info->{bass_ord} -= $key_ord % $p->intervals;
1047             }
1048             $info->{bass_canon} = $info->{bass} =
1049 0         0 $p->root_canon( $info->{bass_ord}, $info->{bass_mod} >= 0 );
1050             }
1051 20         80 $info->{name} = $info->{name_canon} = $info->canonical;
1052 20         60 $info->{system} = $p->{system};
1053 20         78 bless $info => $p->{target};
1054             # ::dump($info);
1055             #warn("_<_XCODE = $xcode, CHORD = ", $info->canonical);
1056 20         80 return $info;
1057             }
1058              
1059 767     767   1345 sub chord_display ( $self ) {
  767         1749  
  767         1267  
1060              
1061             $self->SUPER::chord_display
1062             ( $::config->{"chord-formats"}->{common}
1063 767   33     4750 // $::config->{settings}->{"chord-format"}
      0        
1064             // "%{name}" );
1065             }
1066              
1067             ################ Chord objects: Nashville ################
1068              
1069             package ChordPro::Chord::Nashville;
1070              
1071             our @ISA = 'ChordPro::Chord::Base';
1072              
1073 0     0   0 sub transpose ( $self, $dummy1, $dummy2=0 ) { $self }
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
1074              
1075             sub show {
1076 0     0   0 Carp::croak("call canonical instead of show");
1077             }
1078              
1079 0     0   0 sub canonical ( $self ) {
  0         0  
  0         0  
1080 0         0 my $res = $self->{root_canon} . $self->{qual} . $self->{ext};
1081 0 0 0     0 if ( $self->{bass} && $self->{bass} ne "" ) {
1082 0         0 $res .= "/" . lc($self->{bass});
1083             }
1084 0         0 return $res;
1085             }
1086              
1087 4     4   9 sub chord_display ( $self ) {
  4         10  
  4         8  
1088              
1089             $self->SUPER::chord_display
1090             ( $::config->{"chord-formats"}->{nashville}
1091 4   50     37 // "%{name}" );
1092             }
1093              
1094             ################ Chord objects: Roman ################
1095              
1096             package ChordPro::Chord::Roman;
1097              
1098             our @ISA = 'ChordPro::Chord::Base';
1099              
1100 0     0   0 sub transpose ( $self, $dummy1, $dummy2=0 ) { $self }
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
1101              
1102             sub show {
1103 0     0   0 Carp::croak("call canonical instead of show");
1104             }
1105              
1106 0     0   0 sub canonical ( $self ) {
  0         0  
  0         0  
1107 0         0 my $res = $self->{root_canon} . $self->{qual} . $self->{ext};
1108 0 0 0     0 if ( $self->{bass} && $self->{bass} ne "" ) {
1109 0         0 $res .= "/" . lc($self->{bass});
1110             }
1111 0         0 return $res;
1112             }
1113              
1114 4     4   8 sub chord_display ( $self ) {
  4         8  
  4         9  
1115              
1116             $self->SUPER::chord_display
1117             ( $::config->{"chord-formats"}->{roman}
1118 4   50     35 // "%{name}" );
1119             }
1120              
1121             ################ Chord objects: Annotations ################
1122              
1123             package ChordPro::Chord::Annotation;
1124              
1125 90     90   1076 use String::Interpolate::Named;
  90         209  
  90         39564  
1126              
1127             our @ISA = 'ChordPro::Chord::Base';
1128              
1129 0     0   0 sub transpose ( $self, $dummy1, $dummy2=0 ) { $self }
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
1130 0     0   0 sub transcode ( $self, $dummy1, $dummy2=0 ) { $self }
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
1131              
1132 0     0   0 sub canonical ( $self ) {
  0         0  
  0         0  
1133 0         0 my $res = $self->{text};
1134 0         0 return $res;
1135             }
1136              
1137 2     2   4 sub chord_display ( $self ) {
  2         5  
  2         7  
1138 2         15 return interpolate( { args => $self }, $self->{text} );
1139             }
1140              
1141             # For convenience.
1142 0     0   0 sub is_chord ( $self ) { 0 };
  0         0  
  0         0  
  0         0  
1143 3     3   6 sub is_annotation ( $self ) { 1 };
  3         7  
  3         6  
  3         20  
1144              
1145             ################ Chord objects: Strums ################
1146              
1147             package ChordPro::Chord::Strum;
1148              
1149             # Special 'chord'-like objects for strums in grids.
1150             #
1151             # Main purpose is to show an arrow from the ChordProSymbols font.
1152              
1153             our @ISA = 'ChordPro::Chord::Base';
1154              
1155 90     90   55968 use ChordPro::Symbols qw( strum );
  90         344  
  90         470  
1156              
1157 0     0   0 sub new( $pkg, $data ) {
  0         0  
  0         0  
  0         0  
1158 0         0 my $self = $pkg->SUPER::new( $data );
1159 0         0 my $fmt = strum( $data->{name} );
1160 0 0       0 unless ( defined $fmt ) {
1161 0         0 warn("Unknown strum: $data->{name}\n");
1162 0         0 $self->{format} = "";
1163             }
1164             else {
1165 0         0 $self->{format} = $fmt;
1166             }
1167 0         0 return $self;
1168             }
1169              
1170 0     0   0 sub chord_display ( $self, $default = undef ) {
  0         0  
  0         0  
  0         0  
1171 0         0 $self->{format};
1172             }
1173              
1174 0     0   0 sub transpose ( $self, $dummy1, $dummy2=0 ) { $self }
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
1175 0     0   0 sub transcode ( $self, $dummy1, $dummy2=0 ) { $self }
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
1176              
1177 0     0   0 sub canonical ( $self ) {
  0         0  
  0         0  
1178 0         0 my $res = $self->{text};
1179 0         0 return $res;
1180             }
1181              
1182             # For convenience.
1183 0     0   0 sub is_chord ( $self ) { 0 };
  0         0  
  0         0  
  0         0  
1184 0     0   0 sub is_annotation ( $self ) { 1 };
  0         0  
  0         0  
  0         0  
1185 0     0   0 sub is_nc ( $self ) { 1 };
  0         0  
  0         0  
  0         0  
1186 0     0   0 sub is_xpxc ( $self ) { 0 };
  0         0  
  0         0  
  0         0  
1187 0     0   0 sub has_diagram ( $self ) { 0 };
  0         0  
  0         0  
  0         0  
1188 0     0   0 sub is_gridstrum ( $self ) { 1 };
  0         0  
  0         0  
  0         0  
1189              
1190             ################ Chord objects: NC ################
1191              
1192             package ChordPro::Chord::NC;
1193              
1194 90     90   852 use String::Interpolate::Named;
  90         224  
  90         105220  
1195              
1196             our @ISA = 'ChordPro::Chord::Base';
1197              
1198 0     0   0 sub transpose ( $self, $dummy1, $dummy2=0 ) { $self }
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
1199 0     0   0 sub transcode ( $self, $dummy1, $dummy2=0 ) { $self }
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
1200              
1201 0     0   0 sub canonical ( $self ) {
  0         0  
  0         0  
1202 0         0 my $res = $self->{name};
1203 0         0 return $res;
1204             }
1205              
1206 2     2   5 sub chord_display ( $self ) {
  2         4  
  2         4  
1207 2         13 return interpolate( { args => $self }, $self->{name} );
1208             }
1209              
1210             # For convenience.
1211 7     7   16 sub is_nc ( $self ) { 1 };
  7         17  
  7         26  
  7         30  
1212 0     0   0 sub is_chord ( $self ) { 0 };
  0         0  
  0         0  
  0         0  
1213 2     2   4 sub is_annotation ( $self ) { 0 };
  2         5  
  2         6  
  2         6  
1214 0     0     sub has_diagram ( $self ) { 0 };
  0            
  0            
  0            
1215              
1216             ################ Testing ################
1217              
1218             package main;
1219              
1220             unless ( caller ) {
1221             select(STDERR);
1222             binmode(STDERR, ':utf8');
1223             $::config = { settings => { chordnames => "strict" } };
1224             $::options = { verbose => 2 };
1225             foreach ( @ARGV ) {
1226             if ( $_ eq '-' ) {
1227             $::config = { settings => { chordnames => "relaxed" } };
1228             ChordPro::Chords::Parser->reset_parsers("common");
1229             next;
1230             }
1231             my $p0 = ChordPro::Chords::Parser->default;
1232             my $p1 = ChordPro::Chords::Parser->get_parser("common", 1);
1233             die unless $p0 eq $p1;
1234             my $p2 = ChordPro::Chords::Parser->get_parser("nashville", 1);
1235             my $p3 = ChordPro::Chords::Parser->get_parser("roman", 1);
1236             my $info = $p1->parse($_);
1237             $info = $p2->parse($_) if !$info && $p2;
1238             $info = $p3->parse($_) if !$info && $p3;
1239             print( "$_ => OOPS\n" ), next unless $info;
1240             print( "$_ ($info->{system}) =>" );
1241             print( " ", $info->transcode($_)->canonical, " ($_)" )
1242             for qw( common nashville roman );
1243             print( " '", $info->agnostic, "' (agnostic)\n" );
1244             print( "$_ =>" );
1245             print( " ", $info->transpose($_)->canonical, " ($_)" ) for -2..2;
1246             print( "\n" );
1247             # my $clone = $info->clone;
1248             # delete($clone->{parser});
1249             # print( ::dump($clone), "\n" );
1250             }
1251             }
1252              
1253             1;