File Coverage

blib/lib/Treex/Core/Node/A.pm
Criterion Covered Total %
statement 21 231 9.0
branch 3 80 3.7
condition 2 21 9.5
subroutine 8 59 13.5
pod 12 55 21.8
total 46 446 10.3


line stmt bran cond sub pod time code
1             package Treex::Core::Node::A;
2             $Treex::Core::Node::A::VERSION = '2.20210102';
3 24     24   225 use namespace::autoclean;
  24         69  
  24         567  
4 24     24   3256 use Moose;
  24         85  
  24         231  
5 24     24   171994 use Treex::Core::Common;
  24         80  
  24         254  
6 24     24   142435 use Storable;
  24         97  
  24         107300  
7             extends 'Treex::Core::Node';
8             with 'Treex::Core::Node::Ordered';
9             with 'Treex::Core::Node::InClause';
10             with 'Treex::Core::Node::EffectiveRelations';
11             with 'Treex::Core::Node::Interset' => { interset_attribute => 'iset' };
12              
13             has [qw(form lemma tag no_space_after fused_with_next fused_form fused_misc)] => ( is => 'rw' );
14              
15             has [
16             qw(deprel afun is_parenthesis_root edge_to_collapse is_auxiliary translit ltranslit gloss)
17             ] => ( is => 'rw' );
18              
19             sub get_pml_type_name {
20 79     79 1 175 my ($self) = @_;
21 79 100       261 return $self->is_root() ? 'a-root.type' : 'a-node.type';
22             }
23              
24             # the node is a root of a coordination/apposition construction
25             sub is_coap_root {
26 51     51 1 97 my ($self) = @_;
27 51 50       112 log_fatal('Incorrect number of arguments') if @_ != 1;
28 51   66     1319 return defined $self->afun && $self->afun =~ /^(Coord|Apos)$/;
29             }
30              
31             sub n_node {
32 7     7 1 1047 my ($self) = @_;
33 7         36 my ($first_n_node) = $self->get_referencing_nodes('a.rf');
34 7         55 return $first_n_node;
35             }
36              
37             #------------------------------------------------------------------------------
38             # Figures out the real function of the subtree. If its own afun is AuxP or
39             # AuxC, finds the first descendant with a real afun and returns it. If this is
40             # a coordination or apposition root, finds the first member and returns its
41             # afun (but note that members of the same coordination can differ in afuns if
42             # some of them have 'ExD').
43             #------------------------------------------------------------------------------
44             sub get_real_afun
45             {
46 0     0 1 0 my $self = shift;
47 0         0 my $warnings = shift;
48 0         0 my $afun = $self->afun();
49 0 0       0 if ( not defined($afun) ) {
50 0         0 $afun = '';
51             }
52 0 0       0 if ( $afun =~ m/^Aux[PC]$/ )
    0          
53             {
54 0         0 my @children = $self->children();
55             # Exclude punctuation children (afun-wise, not POS-tag-wise: we do not want to exclude coordination heads).
56 0         0 @children = grep {$_->afun() !~ m/^Aux[XGK]$/} (@children);
  0         0  
57 0         0 my $n = scalar(@children);
58 0 0       0 if ( $n < 1 )
59             {
60 0 0       0 if ($warnings)
61             {
62 0         0 my $i_sentence = $self->get_bundle()->get_position() + 1; # tred numbers from 1
63 0         0 my $form = $self->form();
64 0         0 log_warn("$afun node does not have children (sentence $i_sentence, '$form')");
65             }
66             }
67             else
68             {
69 0 0 0     0 if ( $n > 1 && $warnings )
70             {
71 0         0 my $i_sentence = $self->get_bundle()->get_position() + 1; # tred numbers from 1
72 0         0 my $form = $self->form();
73 0         0 log_warn("$afun node has $n children so it is not clear which one bears the real afun (sentence $i_sentence, '$form')");
74             }
75 0         0 return $children[0]->get_real_afun();
76             }
77             }
78             elsif ( $self->is_coap_root() )
79             {
80 0         0 my @members = $self->get_coap_members();
81 0         0 my $n = scalar(@members);
82 0 0       0 if ( $n < 1 )
83             {
84 0 0       0 if ($warnings)
85             {
86 0         0 my $i_sentence = $self->get_bundle()->get_position() + 1; # tred numbers from 1
87 0         0 my $form = $self->form();
88 0         0 log_warn("$afun does not have members (sentence $i_sentence, '$form')");
89             }
90             }
91             else
92             {
93 0         0 return $members[0]->get_real_afun();
94             }
95             }
96 0         0 return $afun;
97             }
98              
99             #------------------------------------------------------------------------------
100             # Sets the real function of the subtree. If its current afun is AuxP or AuxC,
101             # finds the first descendant with a real afun replaces it. If this is
102             # a coordination or apposition root, finds all the members and replaces their
103             # afuns (but note that members of the same coordination can differ in afuns if
104             # some of them have 'ExD'; this method can only set the same afun for all).
105             #------------------------------------------------------------------------------
106             sub set_real_afun
107             {
108 0     0 1 0 my $self = shift;
109 0         0 my $new_afun = shift;
110 0         0 my $warnings = shift;
111 0         0 my $afun = $self->afun();
112 0 0       0 if ( not defined($afun) ) {
113 0         0 $afun = '';
114             }
115 0 0       0 if ( $afun =~ m/^Aux[PC]$/ )
    0          
116             {
117 0         0 my @children = $self->children();
118             # Exclude punctuation children (afun-wise, not POS-tag-wise: we do not want to exclude coordination heads).
119 0         0 @children = grep {$_->afun() !~ m/^Aux[XGK]$/} (@children);
  0         0  
120 0         0 my $n = scalar(@children);
121 0 0       0 if ( $n < 1 )
122             {
123 0 0       0 if ($warnings)
124             {
125 0         0 my $i_sentence = $self->get_bundle()->get_position() + 1; # tred numbers from 1
126 0         0 my $form = $self->form();
127 0         0 log_warn("$afun node does not have children (sentence $i_sentence, '$form')");
128             }
129             }
130             else
131             {
132 0 0 0     0 if ( $warnings && $n > 1 )
133             {
134 0         0 my $i_sentence = $self->get_bundle()->get_position() + 1; # tred numbers from 1
135 0         0 my $form = $self->form();
136 0         0 log_warn("$afun node has $n children so it is not clear which one bears the real afun (sentence $i_sentence, '$form')");
137             }
138 0         0 foreach my $child (@children)
139             {
140 0         0 $child->set_real_afun($new_afun);
141             }
142 0         0 return;
143             }
144             }
145             elsif ( $self->is_coap_root() )
146             {
147 0         0 my @members = $self->get_coap_members();
148 0         0 my $n = scalar(@members);
149 0 0       0 if ( $n < 1 )
150             {
151 0 0       0 if ($warnings)
152             {
153 0         0 my $i_sentence = $self->get_bundle()->get_position() + 1; # tred numbers from 1
154 0         0 my $form = $self->form();
155 0         0 log_warn("$afun does not have members (sentence $i_sentence, '$form')");
156             }
157             }
158             else
159             {
160 0         0 foreach my $member (@members)
161             {
162 0         0 $member->set_real_afun($new_afun);
163             }
164 0         0 return;
165             }
166             }
167 0         0 $self->set_afun($new_afun);
168 0         0 return $afun;
169             }
170              
171             #------------------------------------------------------------------------------
172             # Recursively copy attributes and children from myself to another node.
173             # This function is specific to the A layer because it contains the list of
174             # attributes. If we could figure out the list automatically, the function would
175             # become general enough to reside directly in Node.pm.
176             #
177             # NOTE: We could possibly make just copy_attributes() layer-dependent and unify
178             # the main copy_atree code.
179             #------------------------------------------------------------------------------
180             sub copy_atree
181             {
182 0     0 1 0 my $self = shift;
183 0         0 my $target = shift;
184              
185             # Copy all attributes of the original node to the new one.
186             # We do this for all the nodes including the ‘root’ (which may actually be
187             # an ordinary node if we are copying a subtree).
188 0         0 $self->copy_attributes($target);
189              
190 0         0 my @children0 = $self->get_children( { ordered => 1 } );
191 0         0 foreach my $child0 (@children0)
192             {
193             # Create a copy of the child node.
194 0         0 my $child1 = $target->create_child();
195             # Call recursively on the subtrees of the children.
196 0         0 $child0->copy_atree($child1);
197             }
198              
199 0         0 return;
200             }
201              
202             #------------------------------------------------------------------------------
203             # Copies values of all attributes from one node to another. The only difference
204             # between the two nodes afterwards should be their ids.
205             #------------------------------------------------------------------------------
206             sub copy_attributes
207             {
208 0     0 0 0 my ( $self, $other ) = @_;
209             # We should copy all attributes that the node has but it is not easy to figure out which these are.
210             # TODO: As a workaround, we list the attributes here directly.
211 0         0 foreach my $attribute (
212             'form', 'lemma', 'tag', 'no_space_after', 'translit', 'ltranslit', 'gloss',
213             'fused_with_next', 'fused_form', 'fused_misc',
214             'ord', 'deprel', 'afun', 'is_member', 'is_parenthesis_root',
215             'conll/deprel', 'conll/cpos', 'conll/pos', 'conll/feat', 'is_shared_modifier', 'morphcat',
216             'clause_number', 'is_clause_head',
217             )
218             {
219 0         0 my $value = $self->get_attr($attribute);
220 0         0 $other->set_attr( $attribute, $value );
221             }
222             # copy values of interset features
223 0         0 my $f = $self->get_iset_structure();
224 0         0 $other->set_iset($f);
225             # deep copy of wild attributes
226 0         0 $other->set_wild( Storable::dclone( $self->wild ) );
227 0         0 return;
228             }
229              
230             # -- linking to p-layer --
231              
232             sub get_terminal_pnode {
233 0     0 1 0 my ($self) = @_;
234 0 0       0 my $p_rf = $self->get_attr('p_terminal.rf') or return;
235 0         0 my $doc = $self->get_document();
236 0         0 return $doc->get_node_by_id($p_rf);
237             }
238              
239             sub set_terminal_pnode {
240 0     0 1 0 my ( $self, $pnode ) = @_;
241 0 0       0 my $new_id = defined $pnode ? $pnode->id : undef;
242 0         0 $self->set_attr( 'p_terminal.rf', $new_id );
243 0         0 return;
244             }
245              
246             sub get_nonterminal_pnodes {
247 0     0 1 0 my ($self) = @_;
248 0 0       0 my $pnode = $self->get_terminal_pnode() or return;
249 0         0 my @nonterminals = ();
250 0         0 while ( $pnode->is_head ) {
251 0         0 $pnode = $pnode->get_parent();
252 0         0 push @nonterminals, $pnode;
253             }
254 0         0 return @nonterminals;
255             }
256              
257             sub get_pnodes {
258 0     0 1 0 my ($self) = @_;
259 0         0 return ( $self->get_terminal_pnode, $self->get_nonterminal_pnodes );
260             }
261              
262             # -- referenced node ids --
263              
264             override '_get_reference_attrs' => sub {
265             my ($self) = @_;
266             return ('p_terminal.rf');
267             };
268              
269             # -- other --
270              
271             # Used only for Czech, so far.
272             sub reset_morphcat {
273 0     0 1 0 my ($self) = @_;
274 0         0 foreach my $category (
275             qw(pos subpos gender number case possgender possnumber
276             person tense grade negation voice reserve1 reserve2)
277             )
278             {
279 0         0 my $old_value = $self->get_attr("morphcat/$category");
280 0 0       0 if ( !defined $old_value ) {
281 0         0 $self->set_attr( "morphcat/$category", '.' );
282             }
283             }
284 0         0 return;
285             }
286              
287             # Used only for reading from PCEDT/PDT trees, so far.
288             sub get_subtree_string {
289 0     0 1 0 my ($self) = @_;
290 0 0       0 return join '', map { defined( $_->form ) ? ( $_->form . ( $_->no_space_after ? '' : ' ' ) ) : '' } $self->get_descendants( { ordered => 1 } );
  0 0       0  
291             }
292              
293             #------------------------------------------------------------------------------
294             # Serializes a tree to a string of dependencies (similar to the Stanford
295             # dependency format). Useful for debugging (quick comparison of two tree
296             # structures and an info string for the error message at the same time).
297             #------------------------------------------------------------------------------
298             sub get_subtree_dependency_string
299             {
300 0     0 0 0 my $self = shift;
301 0         0 my $for_brat = shift; # Do we want a format that spans multiple lines but can be easily visualized in Brat?
302 0         0 my @nodes = $self->get_descendants({'ordered' => 1});
303 0 0       0 my $offset = $for_brat ? 1 : 0;
304             my @dependencies = map
305             {
306 0         0 my $n = $_;
  0         0  
307 0         0 my $no = $n->ord()+$offset;
308 0         0 my $nf = $n->form();
309 0         0 my $p = $n->parent();
310 0         0 my $po = $p->ord()+$offset;
311 0 0       0 my $pf = $p->is_root() ? 'ROOT' : $p->form();
312 0 0       0 my $d = defined($n->deprel()) ? $n->deprel() : defined($n->afun()) ? $n->afun() : defined($n->conll_deprel()) ? $n->conll_deprel() : 'NR';
    0          
    0          
313 0 0       0 if($n->is_member())
314             {
315 0 0       0 if(defined($n->deprel()))
316             {
317 0         0 $d .= ':member';
318             }
319             else
320             {
321 0         0 $d .= '_M';
322             }
323             }
324             "$d($pf-$po, $nf-$no)"
325 0         0 }
326             (@nodes);
327 0         0 my $sentence = join(' ', map {$_->form()} (@nodes));
  0         0  
328 0 0       0 if($for_brat)
329             {
330 0         0 $sentence = "ROOT $sentence";
331 0         0 my $tree = join("\n", @dependencies);
332 0         0 return "~~~ sdparse\n$sentence\n$tree\n~~~\n";
333             }
334             else
335             {
336 0         0 my $tree = join('; ', @dependencies);
337 0         0 return "$sentence\t$tree";
338             }
339             }
340              
341              
342              
343             #------------------------------------------------------------------------------
344             # Says whether this node is member of a fused ("multiword") token.
345             #------------------------------------------------------------------------------
346             sub is_fused
347             {
348 0     0 0 0 my $self = shift;
349 0 0       0 return 1 if($self->fused_with_next());
350 0         0 my $prev = $self->get_prev_node();
351 0   0     0 return defined($prev) && $prev->fused_with_next();
352             }
353              
354              
355              
356             #------------------------------------------------------------------------------
357             # If this node is fused with one or more preceding nodes, returns the first
358             # node of the fusion. Otherwise returns this node.
359             #------------------------------------------------------------------------------
360             sub get_fusion_start
361             {
362 0     0 0 0 my $self = shift;
363 0         0 my $prev = $self->get_prev_node();
364 0 0 0     0 if(defined($prev) && $prev->fused_with_next())
365             {
366 0         0 return $prev->get_fusion_start();
367             }
368 0         0 return $self;
369             }
370              
371              
372              
373             #------------------------------------------------------------------------------
374             # If this node is fused with one or more following nodes, returns the last
375             # node of the fusion. Otherwise returns this node.
376             #------------------------------------------------------------------------------
377             sub get_fusion_end
378             {
379 0     0 0 0 my $self = shift;
380 0 0       0 if($self->fused_with_next())
381             {
382 0         0 my $next = $self->get_next_node();
383 0 0       0 if(defined($next))
384             {
385 0         0 return $next->get_fusion_end();
386             }
387             }
388 0         0 return $self;
389             }
390              
391              
392              
393             #------------------------------------------------------------------------------
394             # Returns list of fused nodes including this node. If the node is not fused
395             # with its neighbors, the list contains only this node.
396             #------------------------------------------------------------------------------
397             sub get_fused_nodes
398             {
399 0     0 0 0 my $self = shift;
400 0         0 my @nodes = ($self);
401 0         0 my $x = $self->get_prev_node();
402 0   0     0 while(defined($x) && $x->fused_with_next())
403             {
404 0         0 unshift(@nodes, $x);
405 0         0 $x = $x->get_prev_node();
406             }
407 0         0 $x = $self;
408 0         0 while($x->fused_with_next())
409             {
410 0         0 $x = $x->get_next_node();
411 0 0       0 last if(!defined($x));
412 0         0 push(@nodes, $x);
413             }
414 0         0 return @nodes;
415             }
416              
417              
418              
419             #------------------------------------------------------------------------------
420             # Returns the fused form stored in the first node of the fusion (multiword
421             # token). If this node is not part of any fusion, returns the fused_form of
422             # this node, which should be undefined.
423             #------------------------------------------------------------------------------
424             sub get_fusion
425             {
426 0     0 0 0 my $self = shift;
427 0         0 return $self->get_fusion_start()->fused_form();
428             }
429              
430              
431              
432             #------------------------------------------------------------------------------
433             # Returns the MISC attributes stored in the first node of the fusion (multiword
434             # token). If this node is not part of any fusion, returns the fused_misc of
435             # this node, which should be undefined.
436             #------------------------------------------------------------------------------
437             sub get_fused_misc
438             {
439 0     0 0 0 my $self = shift;
440 0         0 return $self->get_fusion_start()->fused_misc();
441             }
442              
443              
444              
445             #------------------------------------------------------------------------------
446             # Returns the sentence text, observing the current setting of no_space_after
447             # and of the fused multi-word tokens. That is, this method does not reach to
448             # the sentence attribute of the zone. Instead, it visits all nodes including
449             # $self, puts together their word forms and spaces. The result can be compared
450             # to the zone's sentence attribute, or even used to update the attribute.
451             #------------------------------------------------------------------------------
452             sub collect_sentence_text
453             {
454 0     0 0 0 my $self = shift;
455 0         0 my @nodes = $self->get_root()->get_descendants({'ordered' => 1});
456 0         0 my $text = '';
457 0         0 for(my $i = 0; $i<=$#nodes; $i++)
458             {
459 0         0 my $node = $nodes[$i];
460 0 0 0     0 if($node->is_fused() && $node->get_fusion_start() == $node)
461             {
462 0         0 my $last_node = $node->get_fusion_end();
463 0         0 $text .= $node->get_fusion();
464 0 0       0 $text .= ' ' unless($last_node->no_space_after());
465 0         0 $i += $last_node->ord() - $node->ord();
466             }
467             else
468             {
469 0         0 $text .= $node->form();
470 0 0       0 $text .= ' ' unless($node->no_space_after());
471             }
472             }
473 0         0 $text =~ s/^\s+//;
474 0         0 $text =~ s/\s+$//;
475 0         0 return $text;
476             }
477              
478              
479              
480             #----------- CoNLL attributes -------------
481              
482 3     3 0 12 sub conll_deprel { return $_[0]->get_attr('conll/deprel'); }
483 0     0 0   sub conll_cpos { return $_[0]->get_attr('conll/cpos'); }
484 0     0 0   sub conll_pos { return $_[0]->get_attr('conll/pos'); }
485 0     0 0   sub conll_feat { return $_[0]->get_attr('conll/feat'); }
486              
487 0     0 0   sub set_conll_deprel { return $_[0]->set_attr( 'conll/deprel', $_[1] ); }
488 0     0 0   sub set_conll_cpos { return $_[0]->set_attr( 'conll/cpos', $_[1] ); }
489 0     0 0   sub set_conll_pos { return $_[0]->set_attr( 'conll/pos', $_[1] ); }
490 0     0 0   sub set_conll_feat { return $_[0]->set_attr( 'conll/feat', $_[1] ); }
491              
492             #---------- Morphcat -------------
493              
494 0     0 0   sub morphcat_pos { return $_[0]->get_attr('morphcat/pos'); }
495 0     0 0   sub morphcat_subpos { return $_[0]->get_attr('morphcat/subpos'); }
496 0     0 0   sub morphcat_number { return $_[0]->get_attr('morphcat/number'); }
497 0     0 0   sub morphcat_gender { return $_[0]->get_attr('morphcat/gender'); }
498 0     0 0   sub morphcat_case { return $_[0]->get_attr('morphcat/case'); }
499 0     0 0   sub morphcat_person { return $_[0]->get_attr('morphcat/person'); }
500 0     0 0   sub morphcat_tense { return $_[0]->get_attr('morphcat/tense'); }
501 0     0 0   sub morphcat_negation { return $_[0]->get_attr('morphcat/negation'); }
502 0     0 0   sub morphcat_voice { return $_[0]->get_attr('morphcat/voice'); }
503 0     0 0   sub morphcat_grade { return $_[0]->get_attr('morphcat/grade'); }
504 0     0 0   sub morphcat_mood { return $_[0]->get_attr('morphcat/mood'); }
505 0     0 0   sub morphcat_possnumber { return $_[0]->get_attr('morphcat/possnumber'); }
506 0     0 0   sub morphcat_possgender { return $_[0]->get_attr('morphcat/possgender'); }
507              
508 0     0 0   sub set_morphcat_pos { return $_[0]->set_attr( 'morphcat/pos', $_[1] ); }
509 0     0 0   sub set_morphcat_subpos { return $_[0]->set_attr( 'morphcat/subpos', $_[1] ); }
510 0     0 0   sub set_morphcat_number { return $_[0]->set_attr( 'morphcat/number', $_[1] ); }
511 0     0 0   sub set_morphcat_gender { return $_[0]->set_attr( 'morphcat/gender', $_[1] ); }
512 0     0 0   sub set_morphcat_case { return $_[0]->set_attr( 'morphcat/case', $_[1] ); }
513 0     0 0   sub set_morphcat_person { return $_[0]->set_attr( 'morphcat/person', $_[1] ); }
514 0     0 0   sub set_morphcat_tense { return $_[0]->set_attr( 'morphcat/tense', $_[1] ); }
515 0     0 0   sub set_morphcat_negation { return $_[0]->set_attr( 'morphcat/negation', $_[1] ); }
516 0     0 0   sub set_morphcat_voice { return $_[0]->set_attr( 'morphcat/voice', $_[1] ); }
517 0     0 0   sub set_morphcat_grade { return $_[0]->set_attr( 'morphcat/grade', $_[1] ); }
518 0     0 0   sub set_morphcat_mood { return $_[0]->set_attr( 'morphcat/mood', $_[1] ); }
519 0     0 0   sub set_morphcat_possnumber { return $_[0]->set_attr( 'morphcat/possnumber', $_[1] ); }
520 0     0 0   sub set_morphcat_possgender { return $_[0]->set_attr( 'morphcat/possgender', $_[1] ); }
521              
522             1;
523              
524             __END__
525              
526             ######## QUESTIONABLE / DEPRECATED METHODS ###########
527              
528              
529             # For backward compatibility with PDT-style
530             # TODO: This should be handled in format converters/Readers.
531             sub is_coap_member {
532             my ($self) = @_;
533             log_fatal("Incorrect number of arguments") if @_ != 1;
534             return (
535             $self->is_member
536             || ( ( $self->afun || '' ) =~ /^Aux[CP]$/ && grep { $_->is_coap_member } $self->get_children )
537             )
538             ? 1 : undef;
539             }
540              
541             # deprecated, use get_coap_members
542             sub get_transitive_coap_members { # analogy of PML_T::ExpandCoord
543             my ($self) = @_;
544             log_fatal("Incorrect number of arguments") if @_ != 1;
545             if ( $self->is_coap_root ) {
546             return (
547             map { $_->is_coap_root ? $_->get_transitive_coap_members : ($_) }
548             grep { $_->is_coap_member } $self->get_children
549             );
550             }
551             else {
552              
553             #log_warn("The node ".$self->get_attr('id')." is not root of a coordination/apposition construction\n");
554             return ($self);
555             }
556             }
557              
558             # deprecated, get_coap_members({direct_only})
559             sub get_direct_coap_members {
560             my ($self) = @_;
561             log_fatal("Incorrect number of arguments") if @_ != 1;
562             if ( $self->is_coap_root ) {
563             return ( grep { $_->is_coap_member } $self->get_children );
564             }
565             else {
566              
567             #log_warn("The node ".$self->get_attr('id')." is not root of a coordination/apposition construction\n");
568             return ($self);
569             }
570             }
571              
572             # too easy to implement and too rarely used to be a part of API
573             sub get_transitive_coap_root { # analogy of PML_T::GetNearestNonMember
574             my ($self) = @_;
575             log_fatal("Incorrect number of arguments") if @_ != 1;
576             while ( $self->is_coap_member ) {
577             $self = $self->get_parent;
578             }
579             return $self;
580             }
581              
582              
583             =encoding utf-8
584              
585             =head1 NAME
586              
587             Treex::Core::Node::A
588              
589             =head1 VERSION
590              
591             version 2.20210102
592              
593             =head1 DESCRIPTION
594              
595             a-layer (analytical) node
596              
597             =head1 ATTRIBUTES
598              
599             For each attribute (e.g. C<tag>), there is
600             a getter method (C<< my $tag = $anode->tag(); >>)
601             and a setter method (C<< $anode->set_tag('NN'); >>).
602              
603             =head2 Original w-layer and m-layer attributes
604              
605             =over
606              
607             =item form
608              
609             =item lemma
610              
611             =item tag
612              
613             =item no_space_after
614              
615             =back
616              
617             =head2 Original a-layer attributes
618              
619             =over
620              
621             =item afun
622              
623             =item is_parenthesis_root
624              
625             =item edge_to_collapse
626              
627             =item is_auxiliary
628              
629             =back
630              
631             =head1 METHODS
632              
633             =head2 Links from a-trees to phrase-structure trees
634              
635             =over 4
636              
637             =item $node->get_terminal_pnode
638              
639             Returns a terminal node from the phrase-structure tree
640             that corresponds to the a-node.
641              
642             =item $node->set_terminal_pnode($pnode)
643              
644             Set the given terminal node from the phrase-structure tree
645             as corresponding to the a-node.
646              
647             =item $node->get_nonterminal_pnodes
648              
649             Returns an array of non-terminal nodes from the phrase-structure tree
650             that correspond to the a-node.
651              
652             =item $node->get_pnodes
653              
654             Returns the corresponding terminal node and all non-terminal nodes.
655              
656             =back
657              
658             =head2 Other
659              
660             =over 4
661              
662             =item reset_morphcat
663              
664             =item get_pml_type_name
665              
666             Root and non-root nodes have different PML type in the pml schema
667             (C<a-root.type>, C<a-node.type>)
668              
669             =item is_coap_root
670              
671             Is this node a root (or head) of a coordination/apposition construction?
672             On a-layer this is decided based on C<afun =~ /^(Coord|Apos)$/>.
673              
674             =item get_real_afun()
675              
676             Figures out the real function of the subtree. If its own afun is C<AuxP> or
677             C<AuxC>, finds the first descendant with a real afun and returns it. If this is
678             a coordination or apposition root, finds the first member and returns its
679             afun (but note that members of the same coordination can differ in afuns if
680             some of them have C<ExD>).
681              
682             =item set_real_afun($new_afun)
683              
684             Sets the real function of the subtree. If its current afun is C<AuxP> or C<AuxC>,
685             finds the first descendant with a real afun replaces it. If this is
686             a coordination or apposition root, finds all the members and replaces their
687             afuns (but note that members of the same coordination can differ in afuns if
688             some of them have C<ExD>; this method can only set the same afun for all).
689              
690             =item copy_atree()
691              
692             Recursively copy children from myself to another node.
693             This method is specific to the A layer because it contains the list of
694             attributes. If we could figure out the list automatically, the method would
695             become general enough to reside directly in Node.pm.
696              
697             =item n_node()
698              
699             If this a-node is a part of a named entity,
700             this method returns the corresponding n-node (L<Treex::Core::Node::N>).
701             If this node is a part of more than one named entities,
702             only the most nested one is returned.
703             For example: "Bank of China"
704              
705             $n_node_for_china = $a_node_china->n_node();
706             print $n_node_for_china->get_attr('normalized_name'); # China
707             $n_node_for_bank_of_china = $n_node_for_china->get_parent();
708             print $n_node_for_bank_of_china->get_attr('normalized_name'); # Bank of China
709              
710             =item $node->get_subtree_string
711              
712             Return the string corresponding to a subtree rooted in C<$node>.
713             It's computed based on attributes C<form> and C<no_space_after>.
714              
715             =back
716              
717              
718             =head1 AUTHOR
719              
720             Zdeněk Žabokrtský <zabokrtsky@ufal.mff.cuni.cz>
721              
722             Martin Popel <popel@ufal.mff.cuni.cz>
723              
724             =head1 COPYRIGHT AND LICENSE
725              
726             Copyright © 2011-2012 by Institute of Formal and Applied Linguistics, Charles University in Prague
727              
728             This module is free software; you can redistribute it and/or modify it under the same terms as Perl itself.