line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
package hashtie;
|
2
|
12
|
|
|
12
|
|
75
|
use warnings;
|
|
12
|
|
|
|
|
24
|
|
|
12
|
|
|
|
|
409
|
|
3
|
12
|
|
|
12
|
|
64
|
use strict;
|
|
12
|
|
|
|
|
23
|
|
|
12
|
|
|
|
|
5955
|
|
4
|
|
|
|
|
|
|
#require Tie::Hash;
|
5
|
|
|
|
|
|
|
#our @ISA = qw(Tie::ExtraHash); - Apparently only standard in perl >= 5.10. I'm copying it here to remove that dependency, because
|
6
|
|
|
|
|
|
|
# let's face it, it's eleven lines of code.
|
7
|
|
|
|
|
|
|
# Nota bene: CPAN Testers freaking rock!
|
8
|
|
|
|
|
|
|
|
9
|
|
|
|
|
|
|
sub new {
|
10
|
0
|
|
|
0
|
|
0
|
my $pkg = shift;
|
11
|
0
|
|
|
|
|
0
|
$pkg->TIEHASH(@_);
|
12
|
|
|
|
|
|
|
}
|
13
|
1201
|
|
|
1201
|
|
2038
|
sub TIEHASH { my $p = shift; bless [{}, @_], $p }
|
|
1201
|
|
|
|
|
13649
|
|
14
|
|
|
|
|
|
|
#sub STORE { $_[0][0]{$_[1]} = $_[2] }
|
15
|
|
|
|
|
|
|
#sub FETCH { $_[0][0]{$_[1]} }
|
16
|
0
|
|
|
0
|
|
0
|
sub FIRSTKEY { my $a = scalar keys %{$_[0][0]}; each %{$_[0][0]} }
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
17
|
0
|
|
|
0
|
|
0
|
sub NEXTKEY { each %{$_[0][0]} }
|
|
0
|
|
|
|
|
0
|
|
18
|
108
|
|
|
108
|
|
590
|
sub EXISTS { exists $_[0][0]->{$_[1]} }
|
19
|
0
|
|
|
0
|
|
0
|
sub DELETE { delete $_[0][0]->{$_[1]} }
|
20
|
0
|
|
|
0
|
|
0
|
sub CLEAR { %{$_[0][0]} = () }
|
|
0
|
|
|
|
|
0
|
|
21
|
0
|
|
|
0
|
|
0
|
sub SCALAR { scalar %{$_[0][0]} }
|
|
0
|
|
|
|
|
0
|
|
22
|
|
|
|
|
|
|
|
23
|
|
|
|
|
|
|
# My versions of STORE and FETCH.
|
24
|
|
|
|
|
|
|
sub STORE {
|
25
|
28
|
|
|
28
|
|
62
|
my ($this, $key, $value) = @_;
|
26
|
28
|
100
|
|
|
|
97
|
if ($this->[1]{$key}) { return &{$this->[1]{$key}}(undef, $this->[0], $key, $value); }
|
|
1
|
|
|
|
|
3
|
|
|
1
|
|
|
|
|
28
|
|
27
|
27
|
50
|
|
|
|
165
|
return $this->[2]->setvalue($key, $value) if defined $this->[2];
|
28
|
0
|
|
|
|
|
0
|
$this->[0]{$key} = $value;
|
29
|
|
|
|
|
|
|
}
|
30
|
|
|
|
|
|
|
|
31
|
|
|
|
|
|
|
sub just_store {
|
32
|
52
|
|
|
52
|
|
106
|
my ($this, $key, $value) = @_;
|
33
|
52
|
|
|
|
|
188
|
$this->[0]{$key} = $value;
|
34
|
|
|
|
|
|
|
}
|
35
|
|
|
|
|
|
|
|
36
|
|
|
|
|
|
|
sub FETCH {
|
37
|
45
|
|
|
45
|
|
82
|
my ($this, $key, $value) = @_;
|
38
|
45
|
100
|
|
|
|
133
|
if ($this->[1]{$key}) { return &{$this->[1]{$key}}(undef, $this->[0], $key); }
|
|
4
|
|
|
|
|
8
|
|
|
4
|
|
|
|
|
109
|
|
39
|
41
|
50
|
|
|
|
194
|
return $this->[2]->get_value($key) if defined $this->[2];
|
40
|
0
|
|
|
|
|
0
|
$this->[0]{$key};
|
41
|
|
|
|
|
|
|
}
|
42
|
|
|
|
|
|
|
|
43
|
|
|
|
|
|
|
sub just_get {
|
44
|
148
|
|
|
148
|
|
222
|
my ($this, $key, $value) = @_;
|
45
|
148
|
|
|
|
|
914
|
$this->[0]{$key};
|
46
|
|
|
|
|
|
|
}
|
47
|
|
|
|
|
|
|
|
48
|
|
|
|
|
|
|
package Decl::Node;
|
49
|
|
|
|
|
|
|
|
50
|
12
|
|
|
12
|
|
88
|
use warnings;
|
|
12
|
|
|
|
|
30
|
|
|
12
|
|
|
|
|
348
|
|
51
|
12
|
|
|
12
|
|
67
|
use strict;
|
|
12
|
|
|
|
|
30
|
|
|
12
|
|
|
|
|
441
|
|
52
|
|
|
|
|
|
|
|
53
|
12
|
|
|
12
|
|
17148
|
use Iterator::Simple qw(:all);
|
|
12
|
|
|
|
|
69612
|
|
|
12
|
|
|
|
|
3976
|
|
54
|
12
|
|
|
12
|
|
11892
|
use Text::ParseWords;
|
|
12
|
|
|
|
|
30405
|
|
|
12
|
|
|
|
|
968
|
|
55
|
12
|
|
|
12
|
|
93
|
use Decl::Semantics::Code;
|
|
12
|
|
|
|
|
26
|
|
|
12
|
|
|
|
|
377
|
|
56
|
12
|
|
|
12
|
|
8194
|
use Decl::Util;
|
|
12
|
|
|
|
|
37
|
|
|
12
|
|
|
|
|
1060
|
|
57
|
12
|
|
|
12
|
|
8673
|
use Data::Dumper;
|
|
12
|
|
|
|
|
44727
|
|
|
12
|
|
|
|
|
813
|
|
58
|
12
|
|
|
12
|
|
85
|
use Carp;
|
|
12
|
|
|
|
|
23
|
|
|
12
|
|
|
|
|
1794
|
|
59
|
|
|
|
|
|
|
|
60
|
|
|
|
|
|
|
=head1 NAME
|
61
|
|
|
|
|
|
|
|
62
|
|
|
|
|
|
|
Decl::Node - implements a node in a declarative structure.
|
63
|
|
|
|
|
|
|
|
64
|
|
|
|
|
|
|
=head1 VERSION
|
65
|
|
|
|
|
|
|
|
66
|
|
|
|
|
|
|
Version 0.08
|
67
|
|
|
|
|
|
|
|
68
|
|
|
|
|
|
|
=cut
|
69
|
|
|
|
|
|
|
|
70
|
|
|
|
|
|
|
our $VERSION = '0.08';
|
71
|
|
|
|
|
|
|
|
72
|
|
|
|
|
|
|
|
73
|
|
|
|
|
|
|
=head1 SYNOPSIS
|
74
|
|
|
|
|
|
|
|
75
|
|
|
|
|
|
|
Each node in a C structure is represented by one of these objects. Specific semantics modules subclass these nodes for each of their
|
76
|
|
|
|
|
|
|
components.
|
77
|
|
|
|
|
|
|
|
78
|
|
|
|
|
|
|
=head2 defines(), tags_defined()
|
79
|
|
|
|
|
|
|
|
80
|
|
|
|
|
|
|
Called by C during import, to find out what xmlapi tags this plugin claims to implement. This is a class method, and by default
|
81
|
|
|
|
|
|
|
we've got nothing.
|
82
|
|
|
|
|
|
|
|
83
|
|
|
|
|
|
|
The C function governs how C works.
|
84
|
|
|
|
|
|
|
|
85
|
|
|
|
|
|
|
=cut
|
86
|
0
|
|
|
0
|
1
|
0
|
sub defines { (); }
|
87
|
|
|
|
|
|
|
sub tags_defined {
|
88
|
0
|
|
|
0
|
1
|
0
|
my $self = shift;
|
89
|
0
|
|
|
|
|
0
|
my $tag = Decl->new_data('handle');
|
90
|
0
|
|
|
|
|
0
|
foreach ($self->defines()) {
|
91
|
0
|
|
|
|
|
0
|
$tag->load($_);
|
92
|
|
|
|
|
|
|
}
|
93
|
0
|
|
|
|
|
0
|
return $tag;
|
94
|
|
|
|
|
|
|
}
|
95
|
|
|
|
|
|
|
|
96
|
|
|
|
|
|
|
=head2 overloaded ""
|
97
|
|
|
|
|
|
|
|
98
|
|
|
|
|
|
|
The node class returns tag(class) when expressed as a string.
|
99
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
=cut
|
101
|
|
|
|
|
|
|
|
102
|
12
|
|
|
12
|
|
77
|
use Scalar::Util qw(refaddr);
|
|
12
|
|
|
|
|
20
|
|
|
12
|
|
|
|
|
2659
|
|
103
|
1342
|
|
|
1342
|
|
3805
|
use overload ('""' => sub { $_[0]->tag . '(' . ref($_[0]) . ':' . refaddr($_[0]) . ')' },
|
104
|
17
|
|
|
17
|
|
130
|
'==' => sub { refaddr($_[0]) eq refaddr($_[1]) },
|
105
|
3
|
|
|
3
|
|
394
|
'eq' => sub { refaddr($_[0]) eq refaddr($_[1]) },
|
106
|
12
|
|
|
12
|
|
74
|
'!=' => sub { refaddr($_[0]) ne refaddr($_[1]) });
|
|
12
|
|
|
0
|
|
34
|
|
|
12
|
|
|
|
|
271
|
|
|
0
|
|
|
|
|
0
|
|
107
|
|
|
|
|
|
|
|
108
|
|
|
|
|
|
|
=head2 refaddr_or_undef
|
109
|
|
|
|
|
|
|
|
110
|
|
|
|
|
|
|
This is a cheap trick we're going to use for inserting children after other children.
|
111
|
|
|
|
|
|
|
|
112
|
|
|
|
|
|
|
=cut
|
113
|
|
|
|
|
|
|
|
114
|
|
|
|
|
|
|
sub refaddr_or_undef {
|
115
|
188
|
|
|
188
|
1
|
308
|
my $r = refaddr ($_[0]);
|
116
|
188
|
100
|
|
|
|
348
|
$r = $_[0] if not defined $r;
|
117
|
188
|
|
|
|
|
498
|
$r;
|
118
|
|
|
|
|
|
|
}
|
119
|
|
|
|
|
|
|
|
120
|
|
|
|
|
|
|
=head2 new()
|
121
|
|
|
|
|
|
|
|
122
|
|
|
|
|
|
|
The constructor for a node takes either one or an arrayref containing two texts. If one, it is the entire line-and-body of a node;
|
123
|
|
|
|
|
|
|
if the arrayref, the line and the body are already separated. If they're delivered together, they're split before proceeding.
|
124
|
|
|
|
|
|
|
|
125
|
|
|
|
|
|
|
The line and body are retained, although they may be further parsed later. If the body is parsed, its text is discarded and is reconstructed if it's
|
126
|
|
|
|
|
|
|
needed for self-description. (This can be suppressed if a non-standard parser is used that has no self-description facility.)
|
127
|
|
|
|
|
|
|
|
128
|
|
|
|
|
|
|
The node's I is the first word in the line. The tag determines everything pertaining to this entire section of the
|
129
|
|
|
|
|
|
|
application, including how its contents are parsed.
|
130
|
|
|
|
|
|
|
|
131
|
|
|
|
|
|
|
=cut
|
132
|
|
|
|
|
|
|
|
133
|
|
|
|
|
|
|
sub new {
|
134
|
1201
|
|
|
1201
|
1
|
3973
|
my $class = shift;
|
135
|
|
|
|
|
|
|
#print STDERR "Adding $class\n";
|
136
|
|
|
|
|
|
|
my $self = bless {
|
137
|
|
|
|
|
|
|
state => 'unparsed', # Fresh.
|
138
|
|
|
|
|
|
|
payload => undef, # Not built.
|
139
|
0
|
|
|
0
|
|
0
|
sub => sub {}, # Null action.
|
140
|
1201
|
|
|
|
|
31274
|
callable => 0, # Default is not callable.
|
141
|
|
|
|
|
|
|
owncode => 0, # Default doesn't have own callable code.
|
142
|
|
|
|
|
|
|
macroresult => 0, # Default is explicit text.
|
143
|
|
|
|
|
|
|
flag => '', # Indicates special handling of content.
|
144
|
|
|
|
|
|
|
name => '',
|
145
|
|
|
|
|
|
|
namelist => [],
|
146
|
|
|
|
|
|
|
parameters => {},
|
147
|
|
|
|
|
|
|
parmlist => [],
|
148
|
|
|
|
|
|
|
options => {},
|
149
|
|
|
|
|
|
|
optionlist => [],
|
150
|
|
|
|
|
|
|
label => '',
|
151
|
|
|
|
|
|
|
parser => undef,
|
152
|
|
|
|
|
|
|
code => undef,
|
153
|
|
|
|
|
|
|
finalcode => undef,
|
154
|
|
|
|
|
|
|
errors => [],
|
155
|
|
|
|
|
|
|
elements => [],
|
156
|
|
|
|
|
|
|
parent => undef,
|
157
|
|
|
|
|
|
|
comment => '',
|
158
|
|
|
|
|
|
|
bracket => 0,
|
159
|
|
|
|
|
|
|
replaced => 0,
|
160
|
|
|
|
|
|
|
group => 0,
|
161
|
|
|
|
|
|
|
parsemode => '', # Default is to use class nodes. Other valid values: text, vanilla.
|
162
|
|
|
|
|
|
|
is_reference=> 0,
|
163
|
|
|
|
|
|
|
}, $class;
|
164
|
|
|
|
|
|
|
|
165
|
1201
|
|
|
|
|
3243
|
my %values = ();
|
166
|
1201
|
|
|
|
|
1791
|
my %handlers = ();
|
167
|
1201
|
|
|
|
|
5744
|
$self->{hashtie} = tie %values, 'hashtie', \%handlers, $self;
|
168
|
1201
|
|
|
|
|
3023
|
$self->{v} = \%values;
|
169
|
1201
|
|
|
|
|
2385
|
$self->{h} = \%handlers;
|
170
|
1201
|
|
|
|
|
2593
|
$self->{e} = {};
|
171
|
|
|
|
|
|
|
|
172
|
|
|
|
|
|
|
# Now prepare the body as needed.
|
173
|
1201
|
|
|
|
|
1625
|
my ($line, $body);
|
174
|
1201
|
|
|
|
|
2165
|
$body = shift;
|
175
|
|
|
|
|
|
|
#print STDERR "new: body is " . Dumper ($body);
|
176
|
1201
|
100
|
|
|
|
2982
|
$body = '' unless defined $body;
|
177
|
1201
|
100
|
|
|
|
2639
|
if (ref $body eq 'ARRAY') {
|
178
|
|
|
|
|
|
|
#print STDERR "new: body is arrayref\n";
|
179
|
|
|
|
|
|
|
{
|
180
|
10
|
|
|
|
|
15
|
my @bodyrest;
|
|
10
|
|
|
|
|
15
|
|
181
|
10
|
|
|
|
|
29
|
($line, @bodyrest) = @$body;
|
182
|
|
|
|
|
|
|
#print STDERR "new: first line is $line\n";
|
183
|
10
|
|
|
|
|
28
|
$body = \@bodyrest;
|
184
|
|
|
|
|
|
|
}
|
185
|
|
|
|
|
|
|
} else {
|
186
|
1191
|
|
|
|
|
4794
|
($line, $body) = split /\n/, $body, 2;
|
187
|
|
|
|
|
|
|
}
|
188
|
|
|
|
|
|
|
|
189
|
1201
|
100
|
|
|
|
3433
|
$line = 'node' unless defined $line;
|
190
|
1201
|
|
|
|
|
3168
|
my ($fulltag, $rest) = split /\s+/, $line, 2;
|
191
|
1201
|
|
|
|
|
2891
|
my ($tag, $flag) = splittag ($fulltag);
|
192
|
1201
|
|
|
|
|
3022
|
$self->{tag} = $tag;
|
193
|
1201
|
|
|
|
|
2272
|
$self->{flag} = $flag;
|
194
|
1201
|
|
|
|
|
4723
|
$self->{line} = $rest;
|
195
|
1201
|
100
|
|
|
|
3765
|
$self->{line} = '' if not defined $self->{line};
|
196
|
1201
|
|
|
|
|
2643
|
$self->{body} = $body;
|
197
|
|
|
|
|
|
|
|
198
|
1201
|
|
|
|
|
5014
|
return $self;
|
199
|
|
|
|
|
|
|
}
|
200
|
|
|
|
|
|
|
|
201
|
|
|
|
|
|
|
=head2 splittag - class method
|
202
|
|
|
|
|
|
|
|
203
|
|
|
|
|
|
|
This splits the flag off a tag (e.g. template. => template + .)
|
204
|
|
|
|
|
|
|
|
205
|
|
|
|
|
|
|
=cut
|
206
|
|
|
|
|
|
|
|
207
|
2400
|
|
|
2400
|
1
|
17357
|
sub splittag { $_[0] =~ /^(.*?)([\?\!\*\.\+:]*)$/; }
|
208
|
|
|
|
|
|
|
|
209
|
|
|
|
|
|
|
=head2 tag(), flag(), is($tag), name(), names(), line(), hasbody(), body(), elements(), truenodes(), payload()
|
210
|
|
|
|
|
|
|
|
211
|
|
|
|
|
|
|
Accessor functions.
|
212
|
|
|
|
|
|
|
|
213
|
|
|
|
|
|
|
=cut
|
214
|
|
|
|
|
|
|
|
215
|
12716
|
|
|
12716
|
1
|
67507
|
sub tag { $_[0]->{tag} }
|
216
|
2996
|
100
|
|
2996
|
1
|
18505
|
sub flag { $_[1] ? (index ($_[0]->{flag}, $_[1]) >= 0) : $_[0]->{flag} }
|
217
|
|
|
|
|
|
|
sub is {
|
218
|
23821
|
|
|
23821
|
1
|
32877
|
my ($self, $is) = @_;
|
219
|
23821
|
|
|
|
|
53355
|
foreach (split /\|/, $is) {
|
220
|
23823
|
100
|
|
|
|
79819
|
return 1 if $self->{tag} eq $_;
|
221
|
|
|
|
|
|
|
}
|
222
|
23719
|
|
|
|
|
74447
|
return 0;
|
223
|
|
|
|
|
|
|
}
|
224
|
88
|
|
|
88
|
1
|
1813
|
sub name { $_[0]->{name} }
|
225
|
0
|
|
|
0
|
1
|
0
|
sub names { @{$_[0]->{namelist}} }
|
|
0
|
|
|
|
|
0
|
|
226
|
976
|
|
|
976
|
1
|
6207
|
sub line { $_[0]->{line} }
|
227
|
0
|
0
|
|
0
|
1
|
0
|
sub hasbody { defined $_[0]->{body} ? ($_[0]->{body} ? 1 : 0) : 0 }
|
|
|
0
|
|
|
|
|
|
228
|
2114
|
|
|
2114
|
1
|
8458
|
sub body { $_[0]->{body} }
|
229
|
820
|
|
|
820
|
1
|
1097
|
sub elements { @{$_[0]->{elements}} }
|
|
820
|
|
|
|
|
4385
|
|
230
|
10831
|
50
|
|
10831
|
1
|
11615
|
sub truenodes { grep { ref $_ && (defined $_[1] ? $_->is($_[1]) : 1) } @{$_[0]->{elements}} }
|
|
30851
|
100
|
|
|
|
112574
|
|
|
10831
|
|
|
|
|
34008
|
|
231
|
983
|
|
|
983
|
1
|
3987
|
sub payload { $_[0]->{payload} }
|
232
|
|
|
|
|
|
|
|
233
|
|
|
|
|
|
|
=head2 nodes($flavor)
|
234
|
|
|
|
|
|
|
|
235
|
|
|
|
|
|
|
The I nodes (C of a parent are the actual structural children that aren't comments. This function returns
|
236
|
|
|
|
|
|
|
the I nodes - by using a grouping structure, the results of macros, selects, and inserts can appear to be rooted
|
237
|
|
|
|
|
|
|
in the parent at precisely the place their progenitor is located.
|
238
|
|
|
|
|
|
|
|
239
|
|
|
|
|
|
|
If C<$flavor> is specified, C returns only those children with tags equal to C<$flavor>; otherwise, all functional
|
240
|
|
|
|
|
|
|
children are returned.
|
241
|
|
|
|
|
|
|
|
242
|
|
|
|
|
|
|
=cut
|
243
|
|
|
|
|
|
|
|
244
|
|
|
|
|
|
|
sub nodes {
|
245
|
10823
|
|
|
10823
|
1
|
14656
|
my ($self, $flavor) = @_;
|
246
|
10823
|
|
|
|
|
19743
|
my @return = ();
|
247
|
|
|
|
|
|
|
|
248
|
10823
|
|
|
|
|
23476
|
foreach my $n ($self->truenodes) {
|
249
|
30625
|
100
|
|
|
|
107393
|
if ($n->{group}) {
|
|
|
50
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
250
|
0
|
|
|
|
|
0
|
push @return, $n->nodes($flavor);
|
251
|
|
|
|
|
|
|
} elsif (defined $flavor ? $n->is($flavor) : 1) {
|
252
|
6985
|
|
|
|
|
14532
|
push @return, $n;
|
253
|
|
|
|
|
|
|
}
|
254
|
|
|
|
|
|
|
}
|
255
|
10823
|
100
|
|
|
|
54545
|
return wantarray ? @return : (@return ? $return[0] : undef);
|
|
|
100
|
|
|
|
|
|
256
|
|
|
|
|
|
|
}
|
257
|
|
|
|
|
|
|
|
258
|
|
|
|
|
|
|
=head2 content_nodes($flavor)
|
259
|
|
|
|
|
|
|
|
260
|
|
|
|
|
|
|
The I nodes of a parent are the functional nodes returned by C minus any that have the flag ':'. This permits nodes to be split
|
261
|
|
|
|
|
|
|
into "meta" specifications and child specifications for a given parent. An example might be providing a "style:" parameter for a text structure, or
|
262
|
|
|
|
|
|
|
a "path:" parameter for a directory.
|
263
|
|
|
|
|
|
|
|
264
|
|
|
|
|
|
|
=cut
|
265
|
|
|
|
|
|
|
|
266
|
|
|
|
|
|
|
sub content_nodes {
|
267
|
8
|
|
|
8
|
1
|
23
|
my ($self, $flavor) = @_;
|
268
|
8
|
|
|
|
|
20
|
my @return = ();
|
269
|
|
|
|
|
|
|
|
270
|
8
|
|
|
|
|
40
|
foreach my $n ($self->truenodes) {
|
271
|
23
|
50
|
33
|
|
|
200
|
if ($n->{group} and not $n->flag(':')) {
|
|
|
50
|
33
|
|
|
|
|
|
|
50
|
|
|
|
|
|
272
|
0
|
|
|
|
|
0
|
push @return, $n->content_nodes($flavor);
|
273
|
|
|
|
|
|
|
} elsif ((defined $flavor ? $n->is($flavor) : 1) and not $n->flag(':')) {
|
274
|
23
|
|
|
|
|
61
|
push @return, $n;
|
275
|
|
|
|
|
|
|
}
|
276
|
|
|
|
|
|
|
}
|
277
|
8
|
0
|
|
|
|
53
|
return wantarray ? @return : (@return ? $return[0] : undef);
|
|
|
50
|
|
|
|
|
|
278
|
|
|
|
|
|
|
}
|
279
|
|
|
|
|
|
|
|
280
|
|
|
|
|
|
|
|
281
|
|
|
|
|
|
|
=head2 parent(), ancestry()
|
282
|
|
|
|
|
|
|
|
283
|
|
|
|
|
|
|
A list of all the tags of nodes above this one, culminating in this one's tag, returned as an arrayref.
|
284
|
|
|
|
|
|
|
|
285
|
|
|
|
|
|
|
=cut
|
286
|
|
|
|
|
|
|
|
287
|
7284
|
|
|
7284
|
1
|
23949
|
sub parent { $_[0]->{parent} }
|
288
|
|
|
|
|
|
|
sub ancestry {
|
289
|
646
|
|
|
646
|
1
|
976
|
my ($self) = @_;
|
290
|
646
|
|
|
|
|
1340
|
my $parent = $self->parent();
|
291
|
646
|
50
|
33
|
|
|
2558
|
(defined $parent and $parent != $self->root()) ? [@{$parent->ancestry()}, $self->tag()] : [$self->tag()];
|
|
0
|
|
|
|
|
0
|
|
292
|
|
|
|
|
|
|
}
|
293
|
|
|
|
|
|
|
|
294
|
|
|
|
|
|
|
=head2 parameter($p), option($o), parmlist(), optionlist(), parameter_n(), option_n(), label(), parser(), code(), gencode(), errors(), bracket(), comment()
|
295
|
|
|
|
|
|
|
|
296
|
|
|
|
|
|
|
More accessor functions.
|
297
|
|
|
|
|
|
|
|
298
|
|
|
|
|
|
|
=cut
|
299
|
|
|
|
|
|
|
|
300
|
328
|
100
|
66
|
328
|
1
|
2757
|
sub parameter { $_[0]->{parameters}->{$_[1]} || $_[2] || '' }
|
301
|
8
|
0
|
33
|
8
|
1
|
504
|
sub option { $_[0]->{options}->{$_[1]} || $_[2] || '' }
|
302
|
2
|
|
|
2
|
1
|
869
|
sub option_n { ($_[0]->optionlist)[$_[1]-1] }
|
303
|
3
|
|
|
3
|
1
|
20
|
sub parameter_n { ($_[0]->parmlist)[$_[1]-1] }
|
304
|
57
|
|
|
57
|
1
|
71
|
sub parmlist { @{$_[0]->{parmlist}} }
|
|
57
|
|
|
|
|
228
|
|
305
|
63
|
|
|
63
|
1
|
87
|
sub optionlist { @{$_[0]->{optionlist}} }
|
|
63
|
|
|
|
|
257
|
|
306
|
175
|
|
|
175
|
1
|
3282
|
sub label { $_[0]->{label} }
|
307
|
42
|
|
|
42
|
1
|
184
|
sub parser { $_[0]->{parser} }
|
308
|
116
|
|
|
116
|
1
|
587
|
sub code { $_[0]->{code} }
|
309
|
0
|
|
|
0
|
1
|
0
|
sub gencode { $_[0]->{gencode} }
|
310
|
80
|
|
|
80
|
1
|
323
|
sub bracket { $_[0]->{bracket} }
|
311
|
42
|
|
|
42
|
1
|
128
|
sub comment { $_[0]->{comment} }
|
312
|
|
|
|
|
|
|
|
313
|
0
|
|
|
0
|
1
|
0
|
sub errors { @{$_[0]->{errors}} }
|
|
0
|
|
|
|
|
0
|
|
314
|
|
|
|
|
|
|
|
315
|
|
|
|
|
|
|
=head2 plist(@parameters)
|
316
|
|
|
|
|
|
|
|
317
|
|
|
|
|
|
|
Given a list of parameters, returns a hash (not a hashref) of their values, first looking in the parameters, then looking for children
|
318
|
|
|
|
|
|
|
of the same name and returning their labels if necessary. This allows us to specify a parameter for a given object either like this:
|
319
|
|
|
|
|
|
|
|
320
|
|
|
|
|
|
|
object (parm1=value1, parm2 = value2)
|
321
|
|
|
|
|
|
|
|
322
|
|
|
|
|
|
|
or like this:
|
323
|
|
|
|
|
|
|
|
324
|
|
|
|
|
|
|
object
|
325
|
|
|
|
|
|
|
parm1 "value1"
|
326
|
|
|
|
|
|
|
parm2 "value2"
|
327
|
|
|
|
|
|
|
|
328
|
|
|
|
|
|
|
It just depends on what you find more readable at the time. For this to work during payload build, though, the children have to be built
|
329
|
|
|
|
|
|
|
first, which isn't the default - so you have to call $self->build_children before using this in the payload build.
|
330
|
|
|
|
|
|
|
|
331
|
|
|
|
|
|
|
This is really useful if you're wrapping a module that uses a hash to initialize its object. Like, say, L.
|
332
|
|
|
|
|
|
|
|
333
|
|
|
|
|
|
|
=cut
|
334
|
|
|
|
|
|
|
|
335
|
|
|
|
|
|
|
sub plist {
|
336
|
1
|
|
|
1
|
1
|
8
|
my $self = shift;
|
337
|
1
|
|
|
|
|
3
|
my %p;
|
338
|
1
|
|
|
|
|
3
|
foreach my $p (@_) {
|
339
|
3
|
100
|
|
|
|
10
|
if ($self->parameter($p)) {
|
|
|
50
|
|
|
|
|
|
340
|
2
|
|
|
|
|
6
|
$p{$p} = $self->parameter($p);
|
341
|
|
|
|
|
|
|
} elsif (my $pnode = $self->find($p)) {
|
342
|
1
|
|
|
|
|
6
|
$p{$p} = $pnode->label;
|
343
|
|
|
|
|
|
|
}
|
344
|
|
|
|
|
|
|
}
|
345
|
|
|
|
|
|
|
|
346
|
1
|
|
|
|
|
12
|
%p;
|
347
|
|
|
|
|
|
|
}
|
348
|
|
|
|
|
|
|
|
349
|
|
|
|
|
|
|
=head2 parm_css (parameter), set_css_values (hashref, parameter_string), prepare_css_value (hashref, name), get_css_value (hashref, name)
|
350
|
|
|
|
|
|
|
|
351
|
|
|
|
|
|
|
CSS is characterized by a sort of "parameter tree", where many parameters can be seen as nested in a hierarchy. Take fonts, for example.
|
352
|
|
|
|
|
|
|
A font has a size, a name, a bolded flag, and so on. To specify a font, then, we end up with things like font-name, font-size, font-bold, etc.
|
353
|
|
|
|
|
|
|
In CSS, we can also group those things together and get something like font="name: Times; size: 20", and that is equivalent to
|
354
|
|
|
|
|
|
|
font-name="Times", font-size="20". See?
|
355
|
|
|
|
|
|
|
|
356
|
|
|
|
|
|
|
This function does the same thing with the parameters of a node. If you give it a name "font" it will find /font-*/ as well, and munge
|
357
|
|
|
|
|
|
|
the values into the "font" value. It returns a hashref containing the entire hierarchy of these things, and it will also interpret any
|
358
|
|
|
|
|
|
|
string-type parameters in the higher levels, e.g. font="size: 20; name: Times" will go into {size=>20, name=>'Times'}. Honestly, I love
|
359
|
|
|
|
|
|
|
this way of handling parameters in CSS.
|
360
|
|
|
|
|
|
|
|
361
|
|
|
|
|
|
|
If you give a name "font-size" it will also find any font="size: 20" specification and retrieve the appropriate value.
|
362
|
|
|
|
|
|
|
|
363
|
|
|
|
|
|
|
It I decompose multiple hierarchical levels starting from a string (e.g. something like font="size: {type: 3}" will not be parsed for
|
364
|
|
|
|
|
|
|
font-size-type, because you'd need curly brackets or something anyway, and this ain't JSON, it's just simple CSS-like parameter addressing.
|
365
|
|
|
|
|
|
|
|
366
|
|
|
|
|
|
|
=cut
|
367
|
|
|
|
|
|
|
|
368
|
|
|
|
|
|
|
sub parm_css {
|
369
|
7
|
|
|
7
|
1
|
34
|
my ($self, $parameter) = @_;
|
370
|
7
|
|
|
|
|
14
|
my $return = {};
|
371
|
7
|
|
|
|
|
14
|
my $top = $parameter;
|
372
|
7
|
|
|
|
|
26
|
$top =~ s/[.\-\/].*$//;
|
373
|
7
|
100
|
|
|
|
21
|
hh_set ($return, $top, $self->parameter ($top)) if $self->parameter($top);
|
374
|
7
|
|
|
|
|
23
|
foreach ($self->parmlist()) {
|
375
|
15
|
100
|
|
|
|
118
|
if ($_ =~ /^$top[.\-\/]/) {
|
376
|
10
|
|
|
|
|
24
|
hh_set ($return, $_, $self->parameter ($_));
|
377
|
|
|
|
|
|
|
}
|
378
|
|
|
|
|
|
|
}
|
379
|
7
|
|
|
|
|
27
|
return hh_get ($return, $parameter);
|
380
|
|
|
|
|
|
|
}
|
381
|
|
|
|
|
|
|
|
382
|
|
|
|
|
|
|
|
383
|
|
|
|
|
|
|
=head2 flags({flag=>numeric value, ...}), oflags({flag=>numeric value, ...})
|
384
|
|
|
|
|
|
|
|
385
|
|
|
|
|
|
|
A quick utility to produce an OR'd flag set from a list of parameter words. Pass it a hashref containing numeric values for a set of words, and
|
386
|
|
|
|
|
|
|
you'll get back the OR'd sum of the flags found in the parameters. The C function does this for the parameters (round parens) and the C
|
387
|
|
|
|
|
|
|
function does the same for the options [square brackets].
|
388
|
|
|
|
|
|
|
|
389
|
|
|
|
|
|
|
=cut
|
390
|
|
|
|
|
|
|
|
391
|
|
|
|
|
|
|
sub flags {
|
392
|
0
|
|
|
0
|
1
|
0
|
my ($self, $f) = @_;
|
393
|
|
|
|
|
|
|
|
394
|
0
|
|
|
|
|
0
|
my $r = 0;
|
395
|
|
|
|
|
|
|
|
396
|
0
|
|
|
|
|
0
|
while (my ($k, $v) = each %$f) {
|
397
|
0
|
0
|
|
|
|
0
|
$r |= $v if $self->parameter ($k);
|
398
|
|
|
|
|
|
|
}
|
399
|
0
|
|
|
|
|
0
|
return $r;
|
400
|
|
|
|
|
|
|
}
|
401
|
|
|
|
|
|
|
sub oflags {
|
402
|
0
|
|
|
0
|
1
|
0
|
my ($self, $f) = @_;
|
403
|
|
|
|
|
|
|
|
404
|
0
|
|
|
|
|
0
|
my $r = 0;
|
405
|
|
|
|
|
|
|
|
406
|
0
|
|
|
|
|
0
|
for (my ($k, $v) = each %$f) {
|
407
|
0
|
0
|
|
|
|
0
|
$r |= $v if $self->option ($k);
|
408
|
|
|
|
|
|
|
}
|
409
|
0
|
|
|
|
|
0
|
return $r;
|
410
|
|
|
|
|
|
|
}
|
411
|
|
|
|
|
|
|
|
412
|
|
|
|
|
|
|
=head2 list_parameter ($name)
|
413
|
|
|
|
|
|
|
|
414
|
|
|
|
|
|
|
Sometimes, instead of having e.g. position-x and position-y parameters, it's easier to have something like p=40 20 or dim=20x20. We can use
|
415
|
|
|
|
|
|
|
the C function to obtain a list of any numbers separated by non-number characters. (Note that due to the line parser using
|
416
|
|
|
|
|
|
|
commas to separate the parameters themselves, the separator can't be a comma. Unless you want to write a different line parser, in which
|
417
|
|
|
|
|
|
|
case, go you!)
|
418
|
|
|
|
|
|
|
|
419
|
|
|
|
|
|
|
So the separator characters can be: !@#$%^&*|:;~x and space.
|
420
|
|
|
|
|
|
|
|
421
|
|
|
|
|
|
|
=cut
|
422
|
|
|
|
|
|
|
|
423
|
0
|
|
|
0
|
1
|
0
|
sub list_parameter { split /[!@\#\$%\^\&\*\|:;~xX ]/, parameter(@_); }
|
424
|
|
|
|
|
|
|
|
425
|
|
|
|
|
|
|
=head1 BUILDING STRUCTURE
|
426
|
|
|
|
|
|
|
|
427
|
|
|
|
|
|
|
=head2 load ($string, $after)
|
428
|
|
|
|
|
|
|
|
429
|
|
|
|
|
|
|
The C method loads declarative specification text into a node by calling the parser appropriate to the node. Multiple loads can be carried out,
|
430
|
|
|
|
|
|
|
and will simply add to text already there.
|
431
|
|
|
|
|
|
|
|
432
|
|
|
|
|
|
|
The return value is the list of objects added to the target, if any.
|
433
|
|
|
|
|
|
|
|
434
|
|
|
|
|
|
|
=cut
|
435
|
|
|
|
|
|
|
|
436
|
|
|
|
|
|
|
sub load {
|
437
|
758
|
|
|
758
|
1
|
1861
|
my ($self, $string, $after) = @_;
|
438
|
|
|
|
|
|
|
|
439
|
758
|
|
|
|
|
1258
|
my @added;
|
440
|
|
|
|
|
|
|
|
441
|
758
|
100
|
|
|
|
1877
|
if (ref $string) {
|
442
|
|
|
|
|
|
|
#print STDERR "load: Adding from arrayref!\n" . Dumper($string);
|
443
|
9
|
50
|
|
|
|
35
|
if (ref $string ne 'ARRAY') { # In case we're loading already-created nodes.
|
444
|
0
|
|
|
|
|
0
|
$string->{parent} = $self;
|
445
|
0
|
|
|
|
|
0
|
$self->{elements} = [$self->elements, $string];
|
446
|
0
|
|
|
|
|
0
|
push @added, $string;
|
447
|
|
|
|
|
|
|
} else {
|
448
|
9
|
|
|
|
|
26
|
my $root = $self->root;
|
449
|
9
|
100
|
|
|
|
41
|
$string = [$string] unless ref $$string[0];
|
450
|
9
|
|
|
|
|
23
|
foreach my $addition (@$string) {
|
451
|
|
|
|
|
|
|
#print STDERR "addition is $addition\n";
|
452
|
|
|
|
|
|
|
#print STDERR "line is " . ref($addition) ? $$addition[0] : $addition;
|
453
|
10
|
50
|
|
|
|
36
|
my $tag = ref($addition) ? $$addition[0] : $addition;
|
454
|
10
|
|
|
|
|
55
|
$tag =~ s/ .*//;
|
455
|
|
|
|
|
|
|
#print STDERR ", tag is $tag\n";
|
456
|
|
|
|
|
|
|
|
457
|
|
|
|
|
|
|
# Make and add the tag by hand (for a text body, this is done by the parser in the 'else' block below).
|
458
|
10
|
|
|
|
|
39
|
my $newtag = $root->makenode($self, $tag, $addition);
|
459
|
10
|
|
|
|
|
24
|
$newtag->{parent} = $self;
|
460
|
10
|
|
|
|
|
30
|
$self->{elements} = [$self->elements, $newtag];
|
461
|
|
|
|
|
|
|
|
462
|
10
|
|
|
|
|
49
|
push @added, $newtag;
|
463
|
|
|
|
|
|
|
}
|
464
|
|
|
|
|
|
|
}
|
465
|
|
|
|
|
|
|
} else {
|
466
|
|
|
|
|
|
|
# Taken from the Perl recipes:
|
467
|
749
|
|
|
|
|
1118
|
my ($white, $leader); # common whitespace and common leading string
|
468
|
749
|
100
|
|
|
|
2687
|
if ($string =~ /^\s*(?:([^\w\s]+)(\s*).*\n)(?:\s*\1\2?.*\n)+$/) {
|
469
|
3
|
|
|
|
|
18
|
($white, $leader) = ($2, quotemeta($1));
|
470
|
|
|
|
|
|
|
} else {
|
471
|
746
|
|
|
|
|
3070
|
($white, $leader) = ($string =~ /^(\s+)/, '');
|
472
|
|
|
|
|
|
|
}
|
473
|
749
|
100
|
|
|
|
2324
|
$leader = '' unless $leader;
|
474
|
749
|
100
|
|
|
|
1997
|
$white = '' unless $white;
|
475
|
749
|
|
|
|
|
3252
|
$white =~ s/^\n*//;
|
476
|
749
|
100
|
100
|
|
|
4322
|
$string =~ s/^\s*?$leader(?:$white)?//gm if $leader or $white;
|
477
|
749
|
|
|
|
|
2322
|
my $root = $self->root();
|
478
|
749
|
|
|
|
|
3587
|
@added = $root->parse ($self, $string);
|
479
|
|
|
|
|
|
|
}
|
480
|
|
|
|
|
|
|
|
481
|
758
|
100
|
|
|
|
2103
|
if ($after) {
|
482
|
|
|
|
|
|
|
# Rearrange $self->{elements} if we were given an 'after' node that appears in 'elements'.
|
483
|
6
|
|
|
|
|
11
|
my @newels = ();
|
484
|
6
|
|
|
|
|
10
|
my $found_after = 0;
|
485
|
6
|
|
|
|
|
10
|
foreach my $e (@{$self->{elements}}) {
|
|
6
|
|
|
|
|
18
|
|
486
|
50
|
100
|
|
|
|
111
|
if (refaddr_or_undef($e) eq refaddr_or_undef($after)) {
|
487
|
6
|
|
|
|
|
12
|
$found_after = 1;
|
488
|
6
|
|
|
|
|
14
|
push @newels, $e;
|
489
|
6
|
|
|
|
|
13
|
foreach my $a (@added) {
|
490
|
6
|
|
|
|
|
22
|
push @newels, $a;
|
491
|
|
|
|
|
|
|
}
|
492
|
|
|
|
|
|
|
} else {
|
493
|
44
|
100
|
|
|
|
91
|
last if grep {refaddr_or_undef($_) eq refaddr_or_undef($e)} @added;
|
|
44
|
|
|
|
|
78
|
|
494
|
38
|
|
|
|
|
88
|
push @newels, $e;
|
495
|
|
|
|
|
|
|
}
|
496
|
|
|
|
|
|
|
}
|
497
|
6
|
50
|
|
|
|
31
|
$self->{elements} = \@newels if $found_after;
|
498
|
|
|
|
|
|
|
}
|
499
|
|
|
|
|
|
|
|
500
|
758
|
|
|
|
|
1616
|
foreach (@added) {
|
501
|
927
|
50
|
|
|
|
6064
|
$_->build if $_->can('build');
|
502
|
|
|
|
|
|
|
}
|
503
|
|
|
|
|
|
|
#print Dumper($self->sketch);
|
504
|
757
|
100
|
|
|
|
5898
|
return wantarray ? @added : (@added ? $added[0] : undef);
|
|
|
100
|
|
|
|
|
|
505
|
|
|
|
|
|
|
}
|
506
|
|
|
|
|
|
|
|
507
|
|
|
|
|
|
|
=head2 macroinsert ($spec, $after)
|
508
|
|
|
|
|
|
|
|
509
|
|
|
|
|
|
|
This function adds structure to a given node at runtime that won't show up in the node's C results. It is used by the macro system (hence
|
510
|
|
|
|
|
|
|
the name) but can be used by other runtime structure modifiers that act more or less like macros. The idea is that this structure is meaningful at runtime
|
511
|
|
|
|
|
|
|
but is semantically already accounted for in the existing definition, and should I be generated only at runtime.
|
512
|
|
|
|
|
|
|
|
513
|
|
|
|
|
|
|
=cut
|
514
|
|
|
|
|
|
|
|
515
|
|
|
|
|
|
|
sub macroinsert {
|
516
|
7
|
|
|
7
|
1
|
1690
|
my ($self, $string, $after) = @_;
|
517
|
7
|
|
|
|
|
36
|
my @objects = $self->load($string, $after);
|
518
|
7
|
|
|
|
|
46
|
foreach (@objects) {
|
519
|
9
|
|
|
|
|
30
|
$_->{macroresult} = 1;
|
520
|
|
|
|
|
|
|
}
|
521
|
7
|
|
|
|
|
30
|
@objects;
|
522
|
|
|
|
|
|
|
}
|
523
|
|
|
|
|
|
|
|
524
|
|
|
|
|
|
|
=head2 replace_node ($old_node, $new_node)
|
525
|
|
|
|
|
|
|
|
526
|
|
|
|
|
|
|
There are times when dynamically changing semantics force us to reevaluate an existing node during the build phase. We use C
|
527
|
|
|
|
|
|
|
to replace the existing node with the newly interpeted variant. It works by actual pointer. If the C isn't found, nothing will
|
528
|
|
|
|
|
|
|
happen.
|
529
|
|
|
|
|
|
|
|
530
|
|
|
|
|
|
|
=cut
|
531
|
|
|
|
|
|
|
|
532
|
|
|
|
|
|
|
sub replace_node {
|
533
|
0
|
|
|
0
|
1
|
0
|
my ($self, $old, $new) = @_;
|
534
|
0
|
|
|
|
|
0
|
$old->{replaced} = $new; # Make sure ongoing builds build the right node.
|
535
|
0
|
|
|
|
|
0
|
foreach (@{$self->{elements}}) {
|
|
0
|
|
|
|
|
0
|
|
536
|
0
|
0
|
|
|
|
0
|
next unless ref $_;
|
537
|
0
|
0
|
|
|
|
0
|
$_ = $new if $_ == $old;
|
538
|
|
|
|
|
|
|
}
|
539
|
|
|
|
|
|
|
}
|
540
|
|
|
|
|
|
|
|
541
|
|
|
|
|
|
|
=head2 Setting parts of a node: set_name($name), set_label($label), set_parmlist (@list), set_parameter($key, $value), set_optionlist (@list), set_option($key, $value)
|
542
|
|
|
|
|
|
|
|
543
|
|
|
|
|
|
|
These are handy for building a node from scratch.
|
544
|
|
|
|
|
|
|
|
545
|
|
|
|
|
|
|
=cut
|
546
|
|
|
|
|
|
|
|
547
|
|
|
|
|
|
|
sub set_name {
|
548
|
0
|
|
|
0
|
1
|
0
|
my $self = shift;
|
549
|
0
|
|
|
|
|
0
|
$self->{name} = $_[0];
|
550
|
0
|
|
|
|
|
0
|
$self->{namelist} = [@_]; # Make a copy!
|
551
|
|
|
|
|
|
|
}
|
552
|
|
|
|
|
|
|
sub set_label {
|
553
|
278
|
|
|
278
|
1
|
514
|
my $self = shift;
|
554
|
278
|
|
|
|
|
1246
|
$self->{label} = $_[0];
|
555
|
|
|
|
|
|
|
}
|
556
|
|
|
|
|
|
|
sub set_parmlist {
|
557
|
0
|
|
|
0
|
1
|
0
|
my $self = shift;
|
558
|
0
|
|
|
|
|
0
|
$self->{parmlist} = [@_];
|
559
|
|
|
|
|
|
|
}
|
560
|
|
|
|
|
|
|
sub set_parameter {
|
561
|
0
|
|
|
0
|
1
|
0
|
my ($self, $key, $value) = @_;
|
562
|
0
|
|
|
|
|
0
|
$self->{parameters}->{$key} = $value;
|
563
|
|
|
|
|
|
|
}
|
564
|
|
|
|
|
|
|
sub set_optionlist {
|
565
|
0
|
|
|
0
|
1
|
0
|
my $self = shift;
|
566
|
0
|
|
|
|
|
0
|
$self->{optionlist} = [@_];
|
567
|
|
|
|
|
|
|
}
|
568
|
|
|
|
|
|
|
sub set_option {
|
569
|
0
|
|
|
0
|
1
|
0
|
my ($self, $key, $value) = @_;
|
570
|
0
|
|
|
|
|
0
|
$self->{options}->{$key} = $value;
|
571
|
|
|
|
|
|
|
}
|
572
|
|
|
|
|
|
|
|
573
|
|
|
|
|
|
|
=head2 The build process: build(), preprocess(), preprocess_line(), decode_line(), parse_body(), build_payload(), build_children(), add_to_parent(), post_build()
|
574
|
|
|
|
|
|
|
|
575
|
|
|
|
|
|
|
The C function parses the body of the tag, then builds the payload it defines, then calls build on each child if appropriate, then adds itself
|
576
|
|
|
|
|
|
|
to its parent. It provides the hooks C (checks for macro nature and expresses if so), C (asks the application to call the appropriate
|
577
|
|
|
|
|
|
|
parser for the tag), C (does nothing by default), C (calls C on each element), and C
|
578
|
|
|
|
|
|
|
(does nothing by default).
|
579
|
|
|
|
|
|
|
|
580
|
|
|
|
|
|
|
If this tag corresponds to a macro, then substitution takes place before parsing, in the preprocess step.
|
581
|
|
|
|
|
|
|
|
582
|
|
|
|
|
|
|
=cut
|
583
|
|
|
|
|
|
|
|
584
|
|
|
|
|
|
|
sub build {
|
585
|
984
|
|
|
984
|
1
|
1581
|
my $self = shift;
|
586
|
984
|
50
|
|
|
|
2788
|
return $self->{replaced}->build if $self->{replaced};
|
587
|
|
|
|
|
|
|
|
588
|
984
|
100
|
|
|
|
2816
|
if ($self->{state} ne 'built') {
|
589
|
975
|
100
|
|
|
|
2484
|
if ($self->root()->{macro_definitions}->{$self->tag}) { # This is required because in some cases, the macro definition may have been
|
590
|
|
|
|
|
|
|
# registered *after* the class was already assigned to the macro instance.
|
591
|
|
|
|
|
|
|
# E.g.:
|
592
|
|
|
|
|
|
|
# define my_macro
|
593
|
|
|
|
|
|
|
# ...
|
594
|
|
|
|
|
|
|
# my_macro
|
595
|
|
|
|
|
|
|
# (On the same level with the same parent, my_macro has already been split out.)
|
596
|
4
|
|
|
|
|
19
|
bless $self, 'Decl::Semantics::Macro';
|
597
|
|
|
|
|
|
|
}
|
598
|
975
|
|
|
|
|
2610
|
$self->{force_text} = 0;
|
599
|
975
|
|
|
|
|
2674
|
$self->preprocess_line;
|
600
|
975
|
|
|
|
|
2238
|
$self->decode_line;
|
601
|
975
|
|
|
|
|
3815
|
$self->preprocess;
|
602
|
975
|
50
|
|
|
|
4860
|
$self->parse_body unless $self->{force_text};
|
603
|
975
|
|
|
|
|
3294
|
$self->build_payload;
|
604
|
974
|
100
|
|
|
|
4589
|
$self->build_children unless $self->{force_text};
|
605
|
974
|
|
|
|
|
3182
|
$self->add_to_parent;
|
606
|
974
|
|
|
|
|
3183
|
$self->post_build;
|
607
|
|
|
|
|
|
|
|
608
|
974
|
|
|
|
|
2380
|
$self->{state} = 'built';
|
609
|
|
|
|
|
|
|
}
|
610
|
983
|
|
|
|
|
2634
|
return $self->payload;
|
611
|
|
|
|
|
|
|
}
|
612
|
|
|
|
|
|
|
|
613
|
975
|
|
|
975
|
1
|
1241
|
sub preprocess_line {}
|
614
|
|
|
|
|
|
|
|
615
|
|
|
|
|
|
|
sub decode_line { # Was called parse_line, but there was an unfortunate and brain-bending collision with Text::ParseWords. Oy.
|
616
|
975
|
|
|
975
|
1
|
1362
|
my $self = shift;
|
617
|
975
|
|
|
|
|
1901
|
my $root = $self->root;
|
618
|
975
|
|
|
|
|
3531
|
$root->parse_line ($self);
|
619
|
|
|
|
|
|
|
}
|
620
|
|
|
|
|
|
|
|
621
|
975
|
|
|
975
|
1
|
1482
|
sub preprocess {}
|
622
|
|
|
|
|
|
|
|
623
|
|
|
|
|
|
|
sub parse_body {
|
624
|
975
|
|
|
975
|
1
|
1737
|
my $self = shift;
|
625
|
975
|
50
|
|
|
|
8597
|
if ($self->tag =~ /^!/) {
|
626
|
0
|
|
|
|
|
0
|
$self->{tag} =~ s/^!//;
|
627
|
|
|
|
|
|
|
} else {
|
628
|
975
|
|
|
|
|
2786
|
my $root = $self->root;
|
629
|
975
|
100
|
|
|
|
3305
|
if (ref $self->body eq 'ARRAY') {
|
630
|
|
|
|
|
|
|
# If we have an arrayref input, we don't need to parse it! (2010-12-05)
|
631
|
|
|
|
|
|
|
#print "parse_body: body is an arrayref\n";
|
632
|
10
|
|
|
|
|
21
|
my $list = $self->{body};
|
633
|
10
|
|
|
|
|
23
|
$self->{body} = '';
|
634
|
10
|
|
|
|
|
30
|
foreach (@$list) {
|
635
|
9
|
|
|
|
|
57
|
$self->load ($_);
|
636
|
|
|
|
|
|
|
}
|
637
|
|
|
|
|
|
|
} else {
|
638
|
965
|
100
|
100
|
|
|
2038
|
my @results = $root->parse ($self, $self->body) if $self->body and not $self->{bracket};
|
639
|
965
|
100
|
|
|
|
4633
|
$self->{body} = '' if @results;
|
640
|
|
|
|
|
|
|
}
|
641
|
|
|
|
|
|
|
}
|
642
|
|
|
|
|
|
|
}
|
643
|
|
|
|
|
|
|
|
644
|
942
|
|
|
942
|
1
|
1679
|
sub build_payload {}
|
645
|
|
|
|
|
|
|
|
646
|
|
|
|
|
|
|
sub build_children {
|
647
|
977
|
|
|
977
|
1
|
3584
|
my $self = shift;
|
648
|
|
|
|
|
|
|
|
649
|
977
|
|
|
|
|
2709
|
foreach ($self->nodes) {
|
650
|
55
|
50
|
|
|
|
412
|
$_->build if $_->can('build');
|
651
|
|
|
|
|
|
|
}
|
652
|
|
|
|
|
|
|
}
|
653
|
|
|
|
|
|
|
|
654
|
974
|
|
|
974
|
1
|
1244
|
sub add_to_parent {}
|
655
|
|
|
|
|
|
|
|
656
|
974
|
|
|
974
|
1
|
1263
|
sub post_build {}
|
657
|
|
|
|
|
|
|
|
658
|
|
|
|
|
|
|
=head1 STRUCTURE ACCESS
|
659
|
|
|
|
|
|
|
|
660
|
|
|
|
|
|
|
=head2 find($locator), findbyname($locator)
|
661
|
|
|
|
|
|
|
|
662
|
|
|
|
|
|
|
Given a node, finds a descendant using a simple XPath-like language. Once you build a recursive-descent parser facility into your language, this sort
|
663
|
|
|
|
|
|
|
of thing gets a whole lot easier. The C function looks by tag; the C treats the tag as a type and thus the name as the search
|
664
|
|
|
|
|
|
|
property.
|
665
|
|
|
|
|
|
|
|
666
|
|
|
|
|
|
|
Generation separators are '.', '/', or ':' depending on how you like it. Offsets by number are in round brackets (), while finding children by name is
|
667
|
|
|
|
|
|
|
done with square brackets []. Square brackets [name] find tags named "name". Square brackets [name name2] find name lists (which nodes can have, yes),
|
668
|
|
|
|
|
|
|
and square brackets with an = or =~ can also search for nodes by other values.
|
669
|
|
|
|
|
|
|
|
670
|
|
|
|
|
|
|
You can also pass the results of a parse (the arrayref tree) in as the path; this allows you to build the parse tree using other tools instead of forcing
|
671
|
|
|
|
|
|
|
you to build a string (it also allows a single parse result to be used recursively without having to parse it again).
|
672
|
|
|
|
|
|
|
|
673
|
|
|
|
|
|
|
=cut
|
674
|
|
|
|
|
|
|
|
675
|
|
|
|
|
|
|
sub find {
|
676
|
71
|
|
|
71
|
1
|
2935
|
my ($self, $path) = @_;
|
677
|
|
|
|
|
|
|
|
678
|
71
|
100
|
|
|
|
751
|
$path = $self->root->parse_using ($path, 'locator') unless ref $path;
|
679
|
71
|
100
|
|
|
|
336
|
return $self if @$path == 0;
|
680
|
|
|
|
|
|
|
|
681
|
34
|
|
|
|
|
87
|
my $first = shift @$path;
|
682
|
34
|
|
|
|
|
136
|
foreach ($self->nodes) {
|
683
|
69
|
100
|
|
|
|
286
|
return $_->find($path) if $_->match($first);
|
684
|
|
|
|
|
|
|
}
|
685
|
0
|
|
|
|
|
0
|
return undef;
|
686
|
|
|
|
|
|
|
}
|
687
|
|
|
|
|
|
|
sub findbyname {
|
688
|
8
|
|
|
8
|
1
|
17
|
my ($self, $path) = @_;
|
689
|
|
|
|
|
|
|
|
690
|
8
|
100
|
|
|
|
33
|
$path = $self->root->parse_using ($path, 'locator') unless ref $path;
|
691
|
8
|
100
|
|
|
|
36
|
return $self if @$path == 0;
|
692
|
|
|
|
|
|
|
|
693
|
6
|
|
|
|
|
10
|
my $first = shift @$path;
|
694
|
6
|
|
|
|
|
21
|
foreach ($self->nodes) {
|
695
|
12
|
100
|
|
|
|
41
|
return $_->findbyname($path) if $_->matchbyname($first);
|
696
|
|
|
|
|
|
|
}
|
697
|
0
|
|
|
|
|
0
|
return undef;
|
698
|
|
|
|
|
|
|
}
|
699
|
|
|
|
|
|
|
|
700
|
|
|
|
|
|
|
=head2 match($pathelement), matchbyname($pathelement)
|
701
|
|
|
|
|
|
|
|
702
|
|
|
|
|
|
|
Returns a true value if the node matches the path element specified; otherwise, returns a false value.
|
703
|
|
|
|
|
|
|
|
704
|
|
|
|
|
|
|
=cut
|
705
|
|
|
|
|
|
|
|
706
|
|
|
|
|
|
|
sub match {
|
707
|
6218
|
|
|
6218
|
1
|
8741
|
my ($self, $pathelement) = @_;
|
708
|
6218
|
100
|
|
|
|
17908
|
return ($self->tag eq $pathelement) unless ref $pathelement;
|
709
|
40
|
|
|
|
|
64
|
my ($tag, $name) = @$pathelement;
|
710
|
40
|
100
|
100
|
|
|
84
|
return 1 if $self->tag eq $tag and $self->name eq $name;
|
711
|
29
|
|
|
|
|
103
|
return 0;
|
712
|
|
|
|
|
|
|
}
|
713
|
|
|
|
|
|
|
|
714
|
|
|
|
|
|
|
sub matchbyname {
|
715
|
12
|
|
|
12
|
1
|
21
|
my ($self, $pathelement) = @_;
|
716
|
12
|
50
|
|
|
|
44
|
return ($self->name eq $pathelement) unless ref $pathelement;
|
717
|
0
|
|
|
|
|
0
|
my ($name, $label) = @$pathelement;
|
718
|
0
|
0
|
0
|
|
|
0
|
return 1 if $self->name eq $name and $self->label eq $label;
|
719
|
0
|
|
|
|
|
0
|
return 0;
|
720
|
|
|
|
|
|
|
}
|
721
|
|
|
|
|
|
|
|
722
|
|
|
|
|
|
|
=head2 first($nodename)
|
723
|
|
|
|
|
|
|
|
724
|
|
|
|
|
|
|
Given a node, finds a descendant with the given tag anywhere in its descent. Uses the same path notation as C.
|
725
|
|
|
|
|
|
|
|
726
|
|
|
|
|
|
|
=cut
|
727
|
|
|
|
|
|
|
|
728
|
|
|
|
|
|
|
sub first {
|
729
|
6428
|
|
|
6428
|
1
|
11555
|
my ($self, $path) = @_;
|
730
|
|
|
|
|
|
|
|
731
|
6428
|
100
|
|
|
|
14591
|
$path = $self->root->parse_using ($path, 'locator') unless ref $path;
|
732
|
6428
|
50
|
|
|
|
13378
|
return $self if @$path == 0;
|
733
|
|
|
|
|
|
|
|
734
|
6428
|
|
|
|
|
10717
|
my ($first, @rest) = @$path;
|
735
|
6428
|
|
|
|
|
12111
|
foreach ($self->nodes) {
|
736
|
6149
|
100
|
|
|
|
13076
|
if ($_->match($first)) {
|
737
|
12
|
|
|
|
|
53
|
my $possible = $_->find(\@rest);
|
738
|
12
|
50
|
|
|
|
63
|
return $possible if $possible;
|
739
|
|
|
|
|
|
|
}
|
740
|
6137
|
|
|
|
|
14641
|
my $child = $_->first($path);
|
741
|
6137
|
100
|
|
|
|
15913
|
return $child if $child;
|
742
|
|
|
|
|
|
|
}
|
743
|
6400
|
|
|
|
|
14177
|
return undef;
|
744
|
|
|
|
|
|
|
}
|
745
|
|
|
|
|
|
|
|
746
|
|
|
|
|
|
|
=head2 search($nodename)
|
747
|
|
|
|
|
|
|
|
748
|
|
|
|
|
|
|
Given a node, finds all descendants with the given tag.
|
749
|
|
|
|
|
|
|
|
750
|
|
|
|
|
|
|
=cut
|
751
|
|
|
|
|
|
|
|
752
|
|
|
|
|
|
|
sub search {
|
753
|
3
|
|
|
3
|
1
|
23
|
my ($self, $path) = @_;
|
754
|
3
|
|
|
|
|
6
|
my @returns = ();
|
755
|
3
|
|
|
|
|
7
|
foreach ($self->nodes) {
|
756
|
2
|
50
|
|
|
|
6
|
push @returns, $_ if $_->tag eq $path;
|
757
|
2
|
|
|
|
|
15
|
push @returns, $_->search($path);
|
758
|
|
|
|
|
|
|
}
|
759
|
|
|
|
|
|
|
@returns
|
760
|
3
|
|
|
|
|
11
|
}
|
761
|
|
|
|
|
|
|
|
762
|
|
|
|
|
|
|
=head2 search_data($type)
|
763
|
|
|
|
|
|
|
|
764
|
|
|
|
|
|
|
Given a node, finds all its descendents that match the given type in either name or tag.
|
765
|
|
|
|
|
|
|
If the type ends in a ':', will only return meta nodes.
|
766
|
|
|
|
|
|
|
|
767
|
|
|
|
|
|
|
=cut
|
768
|
|
|
|
|
|
|
|
769
|
|
|
|
|
|
|
sub search_data {
|
770
|
0
|
|
|
0
|
1
|
0
|
my ($self, $type) = @_;
|
771
|
0
|
|
|
|
|
0
|
my $flag = '';
|
772
|
0
|
0
|
|
|
|
0
|
if ($type =~ /:$/) { # TODO: just : flag?
|
773
|
0
|
|
|
|
|
0
|
$type =~ s/:$//;
|
774
|
0
|
|
|
|
|
0
|
$flag = ':';
|
775
|
|
|
|
|
|
|
}
|
776
|
0
|
|
|
|
|
0
|
my @returns = ();
|
777
|
0
|
|
|
|
|
0
|
foreach ($self->nodes) {
|
778
|
0
|
0
|
0
|
|
|
0
|
if ($_->is($type) || $_->name eq ($type)) {
|
779
|
0
|
0
|
0
|
|
|
0
|
push @returns, $_ if not $flag or $_->flag($flag);
|
780
|
|
|
|
|
|
|
}
|
781
|
0
|
|
|
|
|
0
|
push @returns, $_->search_data($type . $flag);
|
782
|
|
|
|
|
|
|
}
|
783
|
0
|
|
|
|
|
0
|
@returns;
|
784
|
|
|
|
|
|
|
}
|
785
|
|
|
|
|
|
|
|
786
|
|
|
|
|
|
|
=head2 describe, myline, describe_content
|
787
|
|
|
|
|
|
|
|
788
|
|
|
|
|
|
|
The C function is used to get our code back out so we can reparse it later if we want to. It includes the body and any children.
|
789
|
|
|
|
|
|
|
The C function just does that without the body and children (just the actual line).
|
790
|
|
|
|
|
|
|
The C function does just the body and children (without the actual line).
|
791
|
|
|
|
|
|
|
|
792
|
|
|
|
|
|
|
We could also use this to check the output of the parser, which notoriously just stops on a line if it encounters something it's not
|
793
|
|
|
|
|
|
|
expecting.
|
794
|
|
|
|
|
|
|
|
795
|
|
|
|
|
|
|
=cut
|
796
|
|
|
|
|
|
|
|
797
|
|
|
|
|
|
|
sub myline {
|
798
|
41
|
|
|
41
|
1
|
54
|
my ($self) = @_;
|
799
|
|
|
|
|
|
|
|
800
|
41
|
|
|
|
|
74
|
my $description = $self->tag . $self->flag;
|
801
|
41
|
|
|
|
|
71
|
foreach (@{$self->{namelist}}) {
|
|
41
|
|
|
|
|
112
|
|
802
|
30
|
|
|
|
|
82
|
$description .= " " . $_;
|
803
|
|
|
|
|
|
|
}
|
804
|
|
|
|
|
|
|
|
805
|
41
|
100
|
|
|
|
106
|
if ($self->parmlist) {
|
806
|
16
|
50
|
|
|
|
35
|
$description .= " (" .
|
|
|
100
|
|
|
|
|
|
807
|
|
|
|
|
|
|
join (', ', map {
|
808
|
4
|
|
|
|
|
14
|
$self->parameter($_) eq 'yes' ?
|
809
|
|
|
|
|
|
|
$_ :
|
810
|
|
|
|
|
|
|
($self->parameter($_) =~ / |"/ ?
|
811
|
|
|
|
|
|
|
$_ . '="' . escapequote ($self->parameter($_)) . '"' :
|
812
|
|
|
|
|
|
|
$_ . '=' . $self->parameter($_))
|
813
|
|
|
|
|
|
|
} $self->parmlist) .
|
814
|
|
|
|
|
|
|
")";
|
815
|
|
|
|
|
|
|
}
|
816
|
|
|
|
|
|
|
|
817
|
41
|
100
|
|
|
|
110
|
if ($self->optionlist) {
|
818
|
8
|
0
|
|
|
|
25
|
$description .= " [" .
|
|
|
50
|
|
|
|
|
|
819
|
|
|
|
|
|
|
join (', ', map {
|
820
|
4
|
|
|
|
|
12
|
$self->option($_) eq 'yes' ?
|
821
|
|
|
|
|
|
|
$_ :
|
822
|
|
|
|
|
|
|
($self->option($_) =~ / |"/ ?
|
823
|
|
|
|
|
|
|
$_ . '="' . escapequote ($self->option($_)) . '"' :
|
824
|
|
|
|
|
|
|
$_ . '=' . $self->option($_))
|
825
|
|
|
|
|
|
|
} $self->optionlist) .
|
826
|
|
|
|
|
|
|
"]";
|
827
|
|
|
|
|
|
|
}
|
828
|
|
|
|
|
|
|
|
829
|
41
|
100
|
|
|
|
95
|
$description .= ' "' . $self->label . '"' if $self->label ne '';
|
830
|
41
|
50
|
|
|
|
95
|
$description .= ' ' . $self->parser . ' <' if $self->parser;
|
831
|
41
|
100
|
|
|
|
87
|
$description .= ' ' . $self->code if $self->code;
|
832
|
41
|
100
|
|
|
|
94
|
$description .= ' ' . $self->bracket if $self->bracket;
|
833
|
41
|
50
|
|
|
|
90
|
$description .= ' ' . $self->comment if $self->comment;
|
834
|
|
|
|
|
|
|
|
835
|
41
|
|
|
|
|
176
|
$description;
|
836
|
|
|
|
|
|
|
}
|
837
|
|
|
|
|
|
|
|
838
|
|
|
|
|
|
|
sub describe {
|
839
|
41
|
|
|
41
|
1
|
59
|
my ($self, $macro_ok) = @_;
|
840
|
|
|
|
|
|
|
|
841
|
41
|
|
|
|
|
117
|
$self->myline . "\n" . $self->describe_content (' ', $macro_ok);
|
842
|
|
|
|
|
|
|
}
|
843
|
|
|
|
|
|
|
sub describe_content {
|
844
|
41
|
|
|
41
|
1
|
75
|
my ($self, $prefix, $macro_ok) = @_;
|
845
|
41
|
|
|
|
|
63
|
my $description = '';
|
846
|
41
|
50
|
|
|
|
105
|
$prefix = '' unless defined $prefix;
|
847
|
41
|
50
|
|
|
|
72
|
$macro_ok = 0 unless defined $macro_ok;
|
848
|
|
|
|
|
|
|
|
849
|
41
|
100
|
|
|
|
83
|
if ($self->body) {
|
850
|
7
|
|
|
|
|
20
|
foreach (split /\n/, $self->body) {
|
851
|
19
|
|
|
|
|
50
|
$description .= "$prefix$_\n";
|
852
|
|
|
|
|
|
|
}
|
853
|
7
|
100
|
|
|
|
20
|
$description .= "}\n" if $self->bracket;
|
854
|
|
|
|
|
|
|
} else {
|
855
|
34
|
|
|
|
|
69
|
foreach ($self->elements) {
|
856
|
29
|
50
|
100
|
|
|
147
|
if (not ref $_) {
|
|
|
100
|
|
|
|
|
|
857
|
0
|
|
|
|
|
0
|
$description .= $_;
|
858
|
|
|
|
|
|
|
} elsif ($_->{macroresult} and not $macro_ok) {
|
859
|
3
|
|
|
|
|
7
|
next;
|
860
|
|
|
|
|
|
|
} else {
|
861
|
26
|
|
|
|
|
72
|
foreach (split /\n/, $_->describe($macro_ok)) {
|
862
|
42
|
|
|
|
|
141
|
$description .= "$prefix$_\n";
|
863
|
|
|
|
|
|
|
}
|
864
|
|
|
|
|
|
|
}
|
865
|
|
|
|
|
|
|
}
|
866
|
|
|
|
|
|
|
}
|
867
|
|
|
|
|
|
|
|
868
|
41
|
|
|
|
|
254
|
$description;
|
869
|
|
|
|
|
|
|
}
|
870
|
|
|
|
|
|
|
|
871
|
|
|
|
|
|
|
=head2 sketch (), sketch_c(), sketch_d()
|
872
|
|
|
|
|
|
|
|
873
|
|
|
|
|
|
|
Returns a thin structure reflecting the nodal structure of the node in question:
|
874
|
|
|
|
|
|
|
|
875
|
|
|
|
|
|
|
['tag',
|
876
|
|
|
|
|
|
|
[['child1', []],
|
877
|
|
|
|
|
|
|
['child2', []]]]
|
878
|
|
|
|
|
|
|
|
879
|
|
|
|
|
|
|
Like that. I'm building it for testing purposes, but it might be useful for something else, too.
|
880
|
|
|
|
|
|
|
|
881
|
|
|
|
|
|
|
The C variant also includes the class of each node, and the C variant runs the
|
882
|
|
|
|
|
|
|
whole thing through Dumper first.
|
883
|
|
|
|
|
|
|
|
884
|
|
|
|
|
|
|
=cut
|
885
|
|
|
|
|
|
|
|
886
|
|
|
|
|
|
|
sub sketch {
|
887
|
40
|
|
|
40
|
1
|
65
|
my ($self) = @_;
|
888
|
|
|
|
|
|
|
|
889
|
40
|
|
|
|
|
89
|
[$self->tag, [map { $_->sketch() } $self->nodes()]];
|
|
35
|
|
|
|
|
111
|
|
890
|
|
|
|
|
|
|
}
|
891
|
|
|
|
|
|
|
sub sketch_c {
|
892
|
0
|
|
|
0
|
1
|
0
|
my ($self) = @_;
|
893
|
|
|
|
|
|
|
|
894
|
0
|
|
|
|
|
0
|
[$self->tag, ref($self), [map { $_->sketch_c() } $self->nodes()]];
|
|
0
|
|
|
|
|
0
|
|
895
|
|
|
|
|
|
|
}
|
896
|
0
|
|
|
0
|
1
|
0
|
sub sketch_d { Dumper ($_[0]->sketch_c); }
|
897
|
|
|
|
|
|
|
|
898
|
|
|
|
|
|
|
=head2 mylocation()
|
899
|
|
|
|
|
|
|
|
900
|
|
|
|
|
|
|
This reports the node's own location in the code tree.
|
901
|
|
|
|
|
|
|
|
902
|
|
|
|
|
|
|
=cut
|
903
|
|
|
|
|
|
|
|
904
|
|
|
|
|
|
|
sub mylocation {
|
905
|
0
|
|
|
0
|
1
|
0
|
my $self = shift;
|
906
|
0
|
|
|
|
|
0
|
my $p = $self->parent->mylocation();
|
907
|
0
|
|
|
|
|
0
|
my $l = $self->tag() . '[' . join(' ', $self->names()) . ']';
|
908
|
0
|
0
|
|
|
|
0
|
return '/' . $l if $p eq '/';
|
909
|
0
|
|
|
|
|
0
|
return $p . '/' . $l;
|
910
|
|
|
|
|
|
|
}
|
911
|
|
|
|
|
|
|
|
912
|
|
|
|
|
|
|
=head2 go($item)
|
913
|
|
|
|
|
|
|
|
914
|
|
|
|
|
|
|
For callable nodes, this is one way to call them. The default is to call the go methods of all the children of the node, in sequence.
|
915
|
|
|
|
|
|
|
The last result is returned as our result (this means that the overall tree may have a return value if you set things up right).
|
916
|
|
|
|
|
|
|
|
917
|
|
|
|
|
|
|
=cut
|
918
|
|
|
|
|
|
|
|
919
|
|
|
|
|
|
|
sub go {
|
920
|
14
|
|
|
14
|
1
|
33
|
my $self = shift;
|
921
|
14
|
|
|
|
|
57
|
my $callcontext = shift;
|
922
|
|
|
|
|
|
|
|
923
|
14
|
|
|
|
|
93
|
$self = $self->deref;
|
924
|
14
|
50
|
|
|
|
50
|
return unless defined $self; # TODO: warning
|
925
|
14
|
50
|
|
|
|
57
|
return unless $self->{callable};
|
926
|
14
|
100
|
66
|
|
|
85
|
return &{$self->{sub}}($callcontext, @_) if $self->{owncode} && $self->{sub};
|
|
6
|
|
|
|
|
211
|
|
927
|
|
|
|
|
|
|
|
928
|
8
|
|
|
|
|
18
|
my $return;
|
929
|
|
|
|
|
|
|
my $last_iffy;
|
930
|
8
|
|
|
|
|
16
|
my $master_iffy = undef;
|
931
|
8
|
|
|
|
|
62
|
foreach ($self->content_nodes) {
|
932
|
23
|
100
|
|
|
|
79
|
next unless $_->{callable};
|
933
|
11
|
100
|
|
|
|
41
|
next if $_->{callable} eq 'sub';
|
934
|
10
|
100
|
|
|
|
44
|
next if $_->{event};
|
935
|
6
|
50
|
|
|
|
33
|
if ($_->{callable} eq '?') {
|
936
|
0
|
|
|
|
|
0
|
$last_iffy = $_;
|
937
|
0
|
0
|
0
|
|
|
0
|
$master_iffy = $_ if $_->flag('!') and not defined $master_iffy;
|
938
|
|
|
|
|
|
|
} else {
|
939
|
6
|
|
|
|
|
56
|
$return = $_->go (@_);
|
940
|
6
|
|
|
|
|
34
|
undef $last_iffy;
|
941
|
|
|
|
|
|
|
}
|
942
|
|
|
|
|
|
|
}
|
943
|
8
|
50
|
|
|
|
33
|
return $master_iffy->go(@_) if defined $master_iffy;
|
944
|
8
|
50
|
|
|
|
30
|
return $last_iffy->go(@_) if defined $last_iffy;
|
945
|
8
|
|
|
|
|
152
|
$return;
|
946
|
|
|
|
|
|
|
}
|
947
|
|
|
|
|
|
|
|
948
|
|
|
|
|
|
|
=head2 closure(...)
|
949
|
|
|
|
|
|
|
|
950
|
|
|
|
|
|
|
For callable nodes, this is the other way to call them; it returns the closure created during initialization. Note that the
|
951
|
|
|
|
|
|
|
default closure is really boring.
|
952
|
|
|
|
|
|
|
|
953
|
|
|
|
|
|
|
=cut
|
954
|
|
|
|
|
|
|
|
955
|
0
|
|
|
0
|
1
|
0
|
sub closure { $_[0]->{sub} }
|
956
|
|
|
|
|
|
|
|
957
|
|
|
|
|
|
|
|
958
|
|
|
|
|
|
|
=head2 iterate()
|
959
|
|
|
|
|
|
|
|
960
|
|
|
|
|
|
|
Returns an L iterator over the body of the node. If the body is a text body, each call returns a line. If the body is a bracketed
|
961
|
|
|
|
|
|
|
code body, it is executed to return an iterable object. Yes, this is neat.
|
962
|
|
|
|
|
|
|
|
963
|
|
|
|
|
|
|
If we're a parser macro, we'll run our special parser over the body instead of the normal parser.
|
964
|
|
|
|
|
|
|
|
965
|
|
|
|
|
|
|
TODO: shouldn't this be recursive for structured nodes?
|
966
|
|
|
|
|
|
|
|
967
|
|
|
|
|
|
|
TODO: might want to do something clever with a code ref tag. (I.e. if the tag is a reference but also has a code block, perhaps evaluate the code
|
968
|
|
|
|
|
|
|
block to figure out the reference or something. This might be a plate of beans.)
|
969
|
|
|
|
|
|
|
|
970
|
|
|
|
|
|
|
=cut
|
971
|
|
|
|
|
|
|
|
972
|
|
|
|
|
|
|
sub iterate {
|
973
|
18
|
|
|
18
|
1
|
1229
|
my $self = shift;
|
974
|
|
|
|
|
|
|
|
975
|
18
|
|
|
|
|
64
|
$self = $self->deref;
|
976
|
18
|
50
|
|
|
|
52
|
return iter([]) unless defined $self; # TODO: warning
|
977
|
18
|
50
|
66
|
|
|
56
|
return iter([]) unless $self->code or $self->nodes or $self->body;
|
|
|
|
66
|
|
|
|
|
978
|
18
|
100
|
66
|
|
|
96
|
if ($self->code or $self->bracket) {
|
|
|
100
|
|
|
|
|
|
979
|
|
|
|
|
|
|
# This is code to be executed, that should return an iterable object.
|
980
|
1
|
|
|
|
|
3
|
my $code;
|
981
|
1
|
50
|
|
|
|
4
|
if ($self->code) {
|
982
|
0
|
|
|
|
|
0
|
$code = $self->code;
|
983
|
|
|
|
|
|
|
} else {
|
984
|
1
|
|
|
|
|
4
|
$code = $self->bracket . "\n";
|
985
|
1
|
|
|
|
|
6
|
$code =~ s/^{//;
|
986
|
1
|
|
|
|
|
6
|
$code .= $self->body;
|
987
|
|
|
|
|
|
|
}
|
988
|
1
|
|
|
|
|
9
|
my $sub = Decl::Semantics::Code::make_code ($self, $code);
|
989
|
1
|
|
|
|
|
34
|
my $result = &$sub();
|
990
|
1
|
50
|
|
|
|
218
|
if (ref $result) {
|
991
|
1
|
|
|
|
|
5
|
return iter ($result);
|
992
|
|
|
|
|
|
|
} else {
|
993
|
0
|
|
|
|
|
0
|
my @lines = split /\n/, $result;
|
994
|
0
|
|
|
|
|
0
|
return iter (\@lines);
|
995
|
|
|
|
|
|
|
}
|
996
|
|
|
|
|
|
|
} elsif ($self->nodes) {
|
997
|
|
|
|
|
|
|
# Iterate over children.
|
998
|
3
|
|
|
|
|
9
|
return ichain map { $_->iterate } $self->nodes;
|
|
17
|
|
|
|
|
198
|
|
999
|
|
|
|
|
|
|
} else {
|
1000
|
|
|
|
|
|
|
# This is text to be iterated over.
|
1001
|
14
|
|
|
|
|
39
|
my @lines = split /\n/, $self->body;
|
1002
|
14
|
|
|
|
|
80
|
return iter (\@lines);
|
1003
|
|
|
|
|
|
|
}
|
1004
|
|
|
|
|
|
|
}
|
1005
|
|
|
|
|
|
|
|
1006
|
|
|
|
|
|
|
=head2 text()
|
1007
|
|
|
|
|
|
|
|
1008
|
|
|
|
|
|
|
This returns a tokenstream on the node's body permitting a consumer to read a series of words interspersed with formatting commands.
|
1009
|
|
|
|
|
|
|
The formatting commands are pretty loose - essentially, "blankline" is the only one. Punctuation is treated as letters in words; that is,
|
1010
|
|
|
|
|
|
|
only whitespace is elided in the tokenization process.
|
1011
|
|
|
|
|
|
|
|
1012
|
|
|
|
|
|
|
If the node has been parsed, it probably doesn't have a body any more, so this will return a blank tokenstream. On the other hand, if the node
|
1013
|
|
|
|
|
|
|
is callable, it will be called, and the result will be used as input to the tokenstream - same rules as C above.
|
1014
|
|
|
|
|
|
|
|
1015
|
|
|
|
|
|
|
=cut
|
1016
|
|
|
|
|
|
|
|
1017
|
|
|
|
|
|
|
|
1018
|
|
|
|
|
|
|
=head2 express(), content()
|
1019
|
|
|
|
|
|
|
|
1020
|
|
|
|
|
|
|
The C function returns the iterated content from iterate(), assembled into lines with as few newlines as possible.
|
1021
|
|
|
|
|
|
|
The C function is normally an alias for C.
|
1022
|
|
|
|
|
|
|
|
1023
|
|
|
|
|
|
|
=cut
|
1024
|
|
|
|
|
|
|
|
1025
|
|
|
|
|
|
|
sub express {
|
1026
|
0
|
|
|
0
|
1
|
0
|
my $self = shift;
|
1027
|
0
|
|
|
|
|
0
|
$self->content(@_);
|
1028
|
|
|
|
|
|
|
}
|
1029
|
|
|
|
|
|
|
sub content {
|
1030
|
3
|
|
|
3
|
1
|
29
|
my ($self, $linebreak) = @_;
|
1031
|
3
|
50
|
|
|
|
12
|
$linebreak = "\n" unless $linebreak;
|
1032
|
|
|
|
|
|
|
|
1033
|
3
|
|
|
|
|
19
|
my $i = $self->iterate;
|
1034
|
3
|
|
|
|
|
200
|
my $result = '';
|
1035
|
3
|
|
|
|
|
7
|
my $line;
|
1036
|
|
|
|
|
|
|
|
1037
|
3
|
|
|
|
|
4
|
do {
|
1038
|
13
|
|
|
|
|
33
|
$line = $i->();
|
1039
|
13
|
100
|
|
|
|
204
|
return $result unless defined $line;
|
1040
|
|
|
|
|
|
|
|
1041
|
10
|
|
|
|
|
17
|
chomp $line;
|
1042
|
10
|
|
|
|
|
38
|
$result .= "$line\n";
|
1043
|
|
|
|
|
|
|
} while (defined $line);
|
1044
|
|
|
|
|
|
|
|
1045
|
0
|
|
|
|
|
0
|
return $result;
|
1046
|
|
|
|
|
|
|
}
|
1047
|
|
|
|
|
|
|
|
1048
|
|
|
|
|
|
|
#my $linestart = 1; TODO: figure out why I thought this should be the default. Sigh.
|
1049
|
|
|
|
|
|
|
#do {
|
1050
|
|
|
|
|
|
|
# $line = $i->();
|
1051
|
|
|
|
|
|
|
# if (defined $line) {
|
1052
|
|
|
|
|
|
|
# if ($self->parameter('raw')) {
|
1053
|
|
|
|
|
|
|
# $result .= $line . "\n";
|
1054
|
|
|
|
|
|
|
# } else {
|
1055
|
|
|
|
|
|
|
# $line =~ s/\s+$//;
|
1056
|
|
|
|
|
|
|
# if ($line ne '') {
|
1057
|
|
|
|
|
|
|
# $result .= ($linestart ? '' : ' ') . $line;
|
1058
|
|
|
|
|
|
|
# $linestart = 0;
|
1059
|
|
|
|
|
|
|
# } else {
|
1060
|
|
|
|
|
|
|
# $result .= $linebreak;
|
1061
|
|
|
|
|
|
|
# $linestart = 1;
|
1062
|
|
|
|
|
|
|
# }
|
1063
|
|
|
|
|
|
|
# }
|
1064
|
|
|
|
|
|
|
# }
|
1065
|
|
|
|
|
|
|
#} while (defined $line);
|
1066
|
|
|
|
|
|
|
|
1067
|
|
|
|
|
|
|
|
1068
|
|
|
|
|
|
|
our $ACCEPT_EVENTS = 0;
|
1069
|
|
|
|
|
|
|
|
1070
|
|
|
|
|
|
|
=head2 event_context
|
1071
|
|
|
|
|
|
|
|
1072
|
|
|
|
|
|
|
If the node is an event context (e.g. a window or frame or dialog), this should return the payload of the node.
|
1073
|
|
|
|
|
|
|
Otherwise, it returns the event_context of the parent node.
|
1074
|
|
|
|
|
|
|
|
1075
|
|
|
|
|
|
|
=cut
|
1076
|
|
|
|
|
|
|
|
1077
|
|
|
|
|
|
|
sub event_context {
|
1078
|
57
|
50
|
|
57
|
1
|
194
|
return $_[0] if $ACCEPT_EVENTS;
|
1079
|
57
|
50
|
|
|
|
157
|
return $_[0]->parent()->event_context() if $_[0]->parent;
|
1080
|
0
|
|
|
|
|
0
|
$_[0]->root;
|
1081
|
|
|
|
|
|
|
}
|
1082
|
|
|
|
|
|
|
|
1083
|
|
|
|
|
|
|
=head2 root
|
1084
|
|
|
|
|
|
|
|
1085
|
|
|
|
|
|
|
Returns the parent - all nodes do this. The top node at C returns itself.
|
1086
|
|
|
|
|
|
|
|
1087
|
|
|
|
|
|
|
=cut
|
1088
|
|
|
|
|
|
|
|
1089
|
4686
|
|
|
4686
|
1
|
10704
|
sub root {$_[0]->parent->root}
|
1090
|
|
|
|
|
|
|
|
1091
|
|
|
|
|
|
|
=head2 error
|
1092
|
|
|
|
|
|
|
|
1093
|
|
|
|
|
|
|
Error handling is the part of programming I'm worst at. But you just have to bite the bullet and address your weaknesses,
|
1094
|
|
|
|
|
|
|
so here is an error marker function. If there's a problem with a node specification, this marks it. Later we'll do something
|
1095
|
|
|
|
|
|
|
sensible with it. TODO: something sensible.
|
1096
|
|
|
|
|
|
|
|
1097
|
|
|
|
|
|
|
=cut
|
1098
|
|
|
|
|
|
|
|
1099
|
|
|
|
|
|
|
sub error {
|
1100
|
0
|
|
|
0
|
1
|
0
|
my ($self, $error) = @_;
|
1101
|
0
|
0
|
|
|
|
0
|
$self->{errors} = [] unless $self->{errors};
|
1102
|
0
|
|
|
|
|
0
|
push @{$self->{errors}}, $error;
|
|
0
|
|
|
|
|
0
|
|
1103
|
|
|
|
|
|
|
#print STDERR "$error\n"; # TODO: bad long-term...
|
1104
|
|
|
|
|
|
|
}
|
1105
|
|
|
|
|
|
|
|
1106
|
|
|
|
|
|
|
=head2 find_data
|
1107
|
|
|
|
|
|
|
|
1108
|
|
|
|
|
|
|
The C function finds a data node starting at a given point in the tree. Right now, it's just going to look for nodes
|
1109
|
|
|
|
|
|
|
by name/tag, but more mature locators should follow eventually.
|
1110
|
|
|
|
|
|
|
|
1111
|
|
|
|
|
|
|
=cut
|
1112
|
|
|
|
|
|
|
|
1113
|
|
|
|
|
|
|
sub find_data {
|
1114
|
4
|
|
|
4
|
1
|
8
|
my ($self, $data) = @_;
|
1115
|
4
|
50
|
|
|
|
13
|
$data = 'data' unless defined $data;
|
1116
|
4
|
0
|
|
|
|
8
|
foreach ($self->nodes) { return ($_, $_->tag) if $_->name eq $data; }
|
|
0
|
|
|
|
|
0
|
|
1117
|
4
|
0
|
|
|
|
11
|
foreach ($self->nodes) { return ($_, $_->tag) if $_->is($data); }
|
|
0
|
|
|
|
|
0
|
|
1118
|
4
|
50
|
|
|
|
11
|
return $self->parent->find_data ($data) if $self->parent;
|
1119
|
0
|
|
|
|
|
0
|
return (undef, undef);
|
1120
|
|
|
|
|
|
|
}
|
1121
|
|
|
|
|
|
|
|
1122
|
|
|
|
|
|
|
=head2 find_context (tag, name)
|
1123
|
|
|
|
|
|
|
|
1124
|
|
|
|
|
|
|
Here, we search for a node with a given name and tag in almost the same way as C - first searching our siblings, then our parent's
|
1125
|
|
|
|
|
|
|
siblings, and so on. Used to look for macro definitions, databases, whatever. If either the tag or the name is omitted, it won't be
|
1126
|
|
|
|
|
|
|
used for comparison (thus the first tag of any name or the first named tag of any type will be returned).
|
1127
|
|
|
|
|
|
|
|
1128
|
|
|
|
|
|
|
Note I said "almost". Any node that comes after the caller won't be considered context. (Neither will the caller itself.) Ditto the parent,
|
1129
|
|
|
|
|
|
|
grandparent, etc. What that means is that context has to appear in the source before the point where C is called.
|
1130
|
|
|
|
|
|
|
|
1131
|
|
|
|
|
|
|
=cut
|
1132
|
|
|
|
|
|
|
|
1133
|
|
|
|
|
|
|
sub find_context {
|
1134
|
0
|
|
|
0
|
1
|
0
|
my ($self, $tag, $name) = @_;
|
1135
|
0
|
0
|
|
|
|
0
|
return unless ($self->parent);
|
1136
|
0
|
|
|
|
|
0
|
foreach ($self->parent->nodes) {
|
1137
|
0
|
0
|
|
|
|
0
|
last if $_ == $self;
|
1138
|
0
|
0
|
0
|
|
|
0
|
return ($_) if ((not defined $tag) || $_->is($tag)) and ((not defined $name) || $_->name eq $name);
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
1139
|
|
|
|
|
|
|
}
|
1140
|
0
|
|
|
|
|
0
|
$self->parent->find_context($tag, $name);
|
1141
|
|
|
|
|
|
|
}
|
1142
|
|
|
|
|
|
|
|
1143
|
|
|
|
|
|
|
|
1144
|
|
|
|
|
|
|
=head2 find_ref (tag, name)
|
1145
|
|
|
|
|
|
|
|
1146
|
|
|
|
|
|
|
The C function looks for tag-and-name combinations that don't have the "is_reference" flag set. It returns the first it finds.
|
1147
|
|
|
|
|
|
|
If either tag or name is C, it ignores that spec.
|
1148
|
|
|
|
|
|
|
|
1149
|
|
|
|
|
|
|
=cut
|
1150
|
|
|
|
|
|
|
|
1151
|
|
|
|
|
|
|
sub find_ref {
|
1152
|
0
|
|
|
0
|
1
|
0
|
my ($self, $tag, $name) = @_;
|
1153
|
0
|
|
|
|
|
0
|
foreach ($self->nodes) {
|
1154
|
0
|
0
|
0
|
|
|
0
|
next if $_->{is_reference} or $_->flag('?');
|
1155
|
0
|
0
|
0
|
|
|
0
|
return $_ if ((not defined $tag) || $_->is($tag)) and ((not defined $name) || $_->name eq $name);
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
1156
|
|
|
|
|
|
|
}
|
1157
|
0
|
|
|
|
|
0
|
$self->parent->find_ref ($tag, $name);
|
1158
|
|
|
|
|
|
|
}
|
1159
|
|
|
|
|
|
|
|
1160
|
|
|
|
|
|
|
=head2 deref ()
|
1161
|
|
|
|
|
|
|
|
1162
|
|
|
|
|
|
|
The C function uses C to dereference a reference tag. If the tag you give it isn't a reference, you'll just get that tag back.
|
1163
|
|
|
|
|
|
|
If it's a dangling reference, you'll get C.
|
1164
|
|
|
|
|
|
|
|
1165
|
|
|
|
|
|
|
=cut
|
1166
|
|
|
|
|
|
|
|
1167
|
|
|
|
|
|
|
sub deref {
|
1168
|
32
|
|
|
32
|
1
|
56
|
my ($self) = @_;
|
1169
|
32
|
50
|
33
|
|
|
194
|
return $self unless $self->{is_reference} or $self->flag('?');
|
1170
|
0
|
0
|
|
|
|
0
|
return $self->parent->find_ref (undef, $self->name) if defined $self->name;
|
1171
|
0
|
|
|
|
|
0
|
return $self->parent->find_ref ($self->tag);
|
1172
|
|
|
|
|
|
|
}
|
1173
|
|
|
|
|
|
|
|
1174
|
|
|
|
|
|
|
=head2 set(), get(), get_pair()
|
1175
|
|
|
|
|
|
|
|
1176
|
|
|
|
|
|
|
These provide a place for object constructors to stash useful information. The C function gets a parameter if the named user variable
|
1177
|
|
|
|
|
|
|
hasn't been set. It also allows the specification of a default value.
|
1178
|
|
|
|
|
|
|
|
1179
|
|
|
|
|
|
|
C gets a pair of named values as an arrayref, with a single arrayref default if neither is found. The individual defaults are assumed
|
1180
|
|
|
|
|
|
|
to be 0.
|
1181
|
|
|
|
|
|
|
|
1182
|
|
|
|
|
|
|
=cut
|
1183
|
|
|
|
|
|
|
|
1184
|
|
|
|
|
|
|
sub set {
|
1185
|
0
|
|
|
0
|
1
|
0
|
my ($self, $var, $value) = @_;
|
1186
|
0
|
|
|
|
|
0
|
$self->{user}->{$var} = $value;
|
1187
|
|
|
|
|
|
|
}
|
1188
|
|
|
|
|
|
|
sub get {
|
1189
|
0
|
|
|
0
|
1
|
0
|
my ($self, $var, $default) = @_;
|
1190
|
0
|
0
|
|
|
|
0
|
return $self->{user}->{$var} if defined $self->{user}->{$var};
|
1191
|
0
|
0
|
|
|
|
0
|
return $self->{parameters}->{$var} if defined $self->{parameters}->{$var};
|
1192
|
0
|
0
|
|
|
|
0
|
return $default if defined $default;
|
1193
|
0
|
|
|
|
|
0
|
''
|
1194
|
|
|
|
|
|
|
}
|
1195
|
|
|
|
|
|
|
sub get_pair {
|
1196
|
0
|
|
|
0
|
1
|
0
|
my ($self, $x, $y, $default) = @_;
|
1197
|
|
|
|
|
|
|
|
1198
|
0
|
0
|
0
|
|
|
0
|
if ($self->get($x) ne '' || $self->get($y) ne '') {
|
1199
|
0
|
|
|
|
|
0
|
return [($self->get($x, 0)), ($self->get($y, 0))];
|
1200
|
|
|
|
|
|
|
}
|
1201
|
0
|
|
|
|
|
0
|
return $default;
|
1202
|
|
|
|
|
|
|
}
|
1203
|
|
|
|
|
|
|
|
1204
|
|
|
|
|
|
|
=head1 VALUES
|
1205
|
|
|
|
|
|
|
|
1206
|
|
|
|
|
|
|
The value system in a Decl node is getting pretty darned complex. Essentially, though, each node has a value lookup hash that either has scalar values directly
|
1207
|
|
|
|
|
|
|
or closures that can be used as proxies for values found in other nodes. (For example, if a node is a macro instantiation, then mostly we're going to be referring
|
1208
|
|
|
|
|
|
|
to values in the definition, not in the instance. If a node hasn't explicitly defined a value but its parent has, then when we set that value we'll want to set it
|
1209
|
|
|
|
|
|
|
in the parent, not in the child. And so on.)
|
1210
|
|
|
|
|
|
|
|
1211
|
|
|
|
|
|
|
When we first want to use a given value in a node, we'll call "find_value". That will return a closure that can be called to get or set the value. If the value
|
1212
|
|
|
|
|
|
|
can't be set, the closure will simply have no effect. The closure will be stashed locally so that it need only be located once, and we're always assured of being
|
1213
|
|
|
|
|
|
|
able to access the same storage location for a given name.
|
1214
|
|
|
|
|
|
|
|
1215
|
|
|
|
|
|
|
=head2 find_value($var), with helper function get_value_closure
|
1216
|
|
|
|
|
|
|
|
1217
|
|
|
|
|
|
|
To find a value:
|
1218
|
|
|
|
|
|
|
|
1219
|
|
|
|
|
|
|
1. Return any previously located closure.
|
1220
|
|
|
|
|
|
|
2. If we're a macro instantiation, look at the macro definition.
|
1221
|
|
|
|
|
|
|
3. See if there's a local definition for the value; return it if so.
|
1222
|
|
|
|
|
|
|
4. See if we have any local constant definitions (our children, evaluated as values).
|
1223
|
|
|
|
|
|
|
5. Check our event context.
|
1224
|
|
|
|
|
|
|
6. If we're still not in luck, ask our parent to do the same.
|
1225
|
|
|
|
|
|
|
7. Otherwise, return "undefined". A set will then create a local variable if necessary.
|
1226
|
|
|
|
|
|
|
|
1227
|
|
|
|
|
|
|
The closure returned by get_value_closure has the same signature as the varhandlers used by the value tag.
|
1228
|
|
|
|
|
|
|
So weird as it sounds, the key and value are in parameters 2 and 3.
|
1229
|
|
|
|
|
|
|
|
1230
|
|
|
|
|
|
|
=cut
|
1231
|
|
|
|
|
|
|
|
1232
|
|
|
|
|
|
|
sub get_value_closure {
|
1233
|
74
|
|
|
74
|
1
|
115
|
my ($self, $value) = @_;
|
1234
|
74
|
50
|
|
|
|
185
|
return $self->{h}->{$value} if exists $self->{h}->{$value};
|
1235
|
74
|
|
|
|
|
174
|
my $v = $self->{hashtie}->just_get($value);
|
1236
|
74
|
100
|
|
|
|
262
|
return $v if ref $v eq 'CODE';
|
1237
|
|
|
|
|
|
|
return sub {
|
1238
|
74
|
100
|
|
74
|
|
212
|
$self->{hashtie}->just_store($value, $_[3]) if defined $_[3];
|
1239
|
74
|
|
|
|
|
197
|
$self->{hashtie}->just_get($value);
|
1240
|
|
|
|
|
|
|
}
|
1241
|
31
|
|
|
|
|
163
|
}
|
1242
|
|
|
|
|
|
|
|
1243
|
|
|
|
|
|
|
sub find_value {
|
1244
|
95
|
|
|
95
|
1
|
139
|
my ($self, $value) = @_;
|
1245
|
|
|
|
|
|
|
|
1246
|
|
|
|
|
|
|
#print STDERR "find_value! $self\n";
|
1247
|
95
|
100
|
|
|
|
258
|
return $self->{h}->{$value} if exists $self->{h}->{$value};
|
1248
|
|
|
|
|
|
|
#print STDERR "0\n";
|
1249
|
91
|
100
|
|
|
|
334
|
return $self->get_value_closure($value) if exists $self->{v}->{$value};
|
1250
|
|
|
|
|
|
|
#print STDERR "1\n";
|
1251
|
|
|
|
|
|
|
|
1252
|
17
|
|
|
|
|
42
|
my $target = $self;
|
1253
|
17
|
50
|
|
|
|
54
|
$target = $self->{instantiates} if $self->{instantiates}; # TODO: maybe. Consider a "context" keyword or sigil or something.
|
1254
|
|
|
|
|
|
|
|
1255
|
|
|
|
|
|
|
#print STDERR "target is actually $target\n";
|
1256
|
17
|
50
|
|
|
|
64
|
if (exists $target->{h}->{$value}) {
|
1257
|
|
|
|
|
|
|
#print STDERR "There is a target handler\n";
|
1258
|
0
|
|
|
|
|
0
|
$self->{h}->{$value} = $target->{h}->{$value};
|
1259
|
0
|
|
|
|
|
0
|
return $self->{v}->{$value};
|
1260
|
|
|
|
|
|
|
}
|
1261
|
17
|
50
|
|
|
|
61
|
if (exists $target->{v}->{$value}) {
|
1262
|
|
|
|
|
|
|
#print STDERR "local pointer " . $target->{v}->{$value} . " found\n";
|
1263
|
0
|
|
|
|
|
0
|
$self->{hashtie}->just_store($value, $target->get_value_closure($value));
|
1264
|
0
|
|
|
|
|
0
|
return $self->{hashtie}->just_get($value);
|
1265
|
|
|
|
|
|
|
}
|
1266
|
|
|
|
|
|
|
|
1267
|
17
|
|
|
|
|
65
|
foreach my $child ($target->nodes) {
|
1268
|
4
|
50
|
33
|
|
|
12
|
if ($child->is($value) or $child->name eq $value) {
|
1269
|
0
|
0
|
|
|
|
0
|
if ($child->label) {
|
1270
|
|
|
|
|
|
|
#print STDERR "local child " . $child->describe . " found\n";
|
1271
|
0
|
|
|
0
|
|
0
|
$self->{hashtie}->just_store ($value, sub { $child->label });
|
|
0
|
|
|
|
|
0
|
|
1272
|
0
|
|
|
|
|
0
|
return $self->{hashtie}->just_get ($value);
|
1273
|
|
|
|
|
|
|
}
|
1274
|
0
|
0
|
|
|
|
0
|
if ($child->describe_content) {
|
1275
|
|
|
|
|
|
|
#print STDERR "local child " . $child->describe . " found\n";
|
1276
|
0
|
|
|
0
|
|
0
|
$self->{hashtie}->just_store ($value, sub { $child->describe_content });
|
|
0
|
|
|
|
|
0
|
|
1277
|
0
|
|
|
|
|
0
|
return $self->{hashtie}->just_get ($value);
|
1278
|
|
|
|
|
|
|
}
|
1279
|
0
|
|
|
|
|
0
|
last;
|
1280
|
|
|
|
|
|
|
}
|
1281
|
|
|
|
|
|
|
}
|
1282
|
|
|
|
|
|
|
|
1283
|
17
|
100
|
|
|
|
57
|
unless ($target->event_context == $target) {
|
1284
|
|
|
|
|
|
|
#print STDERR "We have an event context\n";
|
1285
|
13
|
|
|
|
|
41
|
my $cx = $target->event_context;
|
1286
|
13
|
50
|
|
|
|
61
|
if (exists $cx->{h}->{$value}) {
|
1287
|
|
|
|
|
|
|
#print STDERR "There is a target handler in the cx\n";
|
1288
|
0
|
|
|
|
|
0
|
$self->{h}->{$value} = $cx->{h}->{$value};
|
1289
|
0
|
|
|
|
|
0
|
return $self->{h}->{$value};
|
1290
|
|
|
|
|
|
|
}
|
1291
|
|
|
|
|
|
|
#print STDERR "Looking in event context $cx\n";
|
1292
|
13
|
|
|
|
|
74
|
my $context_value = $cx->find_value($value);
|
1293
|
|
|
|
|
|
|
#print STDERR "3\n";
|
1294
|
13
|
100
|
|
|
|
45
|
if (defined $context_value) {
|
1295
|
|
|
|
|
|
|
#print STDERR "context value $context_value found\n";
|
1296
|
12
|
|
|
|
|
42
|
$self->{hashtie}->just_store ($value, $context_value);
|
1297
|
12
|
|
|
|
|
36
|
return $context_value;
|
1298
|
|
|
|
|
|
|
}
|
1299
|
|
|
|
|
|
|
#print STDERR "Was not defined in event context $cx\n";
|
1300
|
|
|
|
|
|
|
}
|
1301
|
|
|
|
|
|
|
|
1302
|
|
|
|
|
|
|
#print STDERR "Looking in parent\n";
|
1303
|
5
|
100
|
|
|
|
17
|
return $self->parent->find_value($value) if $self->parent;
|
1304
|
|
|
|
|
|
|
#print STDERR "Returning undef\n";
|
1305
|
4
|
|
|
|
|
13
|
return undef;
|
1306
|
|
|
|
|
|
|
}
|
1307
|
|
|
|
|
|
|
|
1308
|
|
|
|
|
|
|
|
1309
|
|
|
|
|
|
|
=head2 value($var), setvalue($var, $value)
|
1310
|
|
|
|
|
|
|
|
1311
|
|
|
|
|
|
|
Accesses the global application value named.
|
1312
|
|
|
|
|
|
|
|
1313
|
|
|
|
|
|
|
=cut
|
1314
|
|
|
|
|
|
|
|
1315
|
12
|
|
|
12
|
1
|
107
|
sub value { $_[0]->{v}->{$_[1]} }
|
1316
|
|
|
|
|
|
|
#sub setvalue { $_[0]->{v}->{$_[1]} = $_[2]; }
|
1317
|
|
|
|
|
|
|
sub setvalue {
|
1318
|
29
|
|
|
29
|
1
|
61
|
my ($self, $value, $newvalue) = @_;
|
1319
|
29
|
50
|
|
|
|
90
|
return if $value =~ /^\*/; # Set has no effect on *-values.
|
1320
|
29
|
|
|
|
|
110
|
my $var = $self->find_value($value);
|
1321
|
29
|
100
|
|
|
|
179
|
return $var->($self, $self->{v}, $value, $newvalue) if defined $var;
|
1322
|
1
|
|
|
|
|
6
|
$self->{hashtie}->just_store($value, $newvalue);
|
1323
|
|
|
|
|
|
|
}
|
1324
|
|
|
|
|
|
|
|
1325
|
|
|
|
|
|
|
=head2 get_value($var)
|
1326
|
|
|
|
|
|
|
|
1327
|
|
|
|
|
|
|
Given the name of a value, we can find it in various places, which we look at in order:
|
1328
|
|
|
|
|
|
|
|
1329
|
|
|
|
|
|
|
- A set value in the node asked
|
1330
|
|
|
|
|
|
|
- Rinse and repeat for the node's parent.
|
1331
|
|
|
|
|
|
|
|
1332
|
|
|
|
|
|
|
Names starting with an asterisk find parts of the node itself: *name, *label, *parameter ,
|
1333
|
|
|
|
|
|
|
*option , *content, and anything else I forgot and add later. A double asterisk gets the same values
|
1334
|
|
|
|
|
|
|
from the parent. Triple asterisk, grandparent, etc.
|
1335
|
|
|
|
|
|
|
|
1336
|
|
|
|
|
|
|
=cut
|
1337
|
|
|
|
|
|
|
|
1338
|
|
|
|
|
|
|
sub get_value {
|
1339
|
52
|
|
|
52
|
1
|
115
|
my ($self, $value) = @_;
|
1340
|
|
|
|
|
|
|
|
1341
|
52
|
50
|
|
|
|
133
|
if ($value =~ /^\*/) {
|
1342
|
0
|
|
|
|
|
0
|
$value =~ s/^\* *//;
|
1343
|
0
|
0
|
|
|
|
0
|
return $self->parent->get_value ($value) if $value =~ /^\*/;
|
1344
|
0
|
0
|
|
|
|
0
|
return $self->name if $value eq 'name';
|
1345
|
0
|
0
|
|
|
|
0
|
return $self->label if $value eq 'label';
|
1346
|
0
|
0
|
|
|
|
0
|
return $self->describe_content('', 0) if $value eq 'content';
|
1347
|
0
|
|
|
|
|
0
|
return undef;
|
1348
|
|
|
|
|
|
|
}
|
1349
|
|
|
|
|
|
|
|
1350
|
52
|
|
|
|
|
128
|
my $var = $self->find_value($value);
|
1351
|
52
|
100
|
|
|
|
178
|
return if not defined $var;
|
1352
|
50
|
|
|
|
|
264
|
$var->($self, $self->{v}, $value);
|
1353
|
|
|
|
|
|
|
}
|
1354
|
|
|
|
|
|
|
|
1355
|
|
|
|
|
|
|
=head2 express_value($valuespec)
|
1356
|
|
|
|
|
|
|
|
1357
|
|
|
|
|
|
|
A full value spec pipes a given value through a series of filters:
|
1358
|
|
|
|
|
|
|
|
1359
|
|
|
|
|
|
|
[|]*
|
1360
|
|
|
|
|
|
|
|
1361
|
|
|
|
|
|
|
A filter is simply a function that takes one parameter. (This is an oversimplification: the filter can be given parameters that are space-delimited.)
|
1362
|
|
|
|
|
|
|
|
1363
|
|
|
|
|
|
|
If no lookup value is desired as a starting value, you can also just start the pipe with a filter/function call. marked with an exclamation mark:
|
1364
|
|
|
|
|
|
|
|
1365
|
|
|
|
|
|
|
![|]*
|
1366
|
|
|
|
|
|
|
|
1367
|
|
|
|
|
|
|
Clear? Clear.
|
1368
|
|
|
|
|
|
|
|
1369
|
|
|
|
|
|
|
=cut
|
1370
|
|
|
|
|
|
|
|
1371
|
|
|
|
|
|
|
sub express_value {
|
1372
|
0
|
|
|
0
|
1
|
0
|
my ($self, $valspec) = @_;
|
1373
|
0
|
|
|
|
|
0
|
my @pieces = split /\|/, $valspec; # TODO: a real parser to permit pipe characters within strings.
|
1374
|
0
|
|
|
|
|
0
|
my $value = '';
|
1375
|
0
|
0
|
|
|
|
0
|
if ($pieces[0] =~ /^!/) {
|
1376
|
0
|
|
|
|
|
0
|
$pieces[0] =~ s/^! *//;
|
1377
|
|
|
|
|
|
|
} else {
|
1378
|
0
|
|
|
|
|
0
|
$value = $self->get_value(shift @pieces);
|
1379
|
|
|
|
|
|
|
}
|
1380
|
0
|
|
|
|
|
0
|
while (my $filter = shift @pieces) {
|
1381
|
0
|
|
|
|
|
0
|
$filter =~ s/^\s*//;
|
1382
|
0
|
|
|
|
|
0
|
$filter =~ s/\s*$//;
|
1383
|
0
|
|
|
|
|
0
|
my @words = parse_line ('\s+', 0, $filter);
|
1384
|
0
|
|
|
|
|
0
|
my $filter = shift @words;
|
1385
|
0
|
|
|
|
|
0
|
$value = $self->call_filter($filter, $value, @words);
|
1386
|
|
|
|
|
|
|
}
|
1387
|
0
|
|
|
|
|
0
|
$value;
|
1388
|
|
|
|
|
|
|
}
|
1389
|
|
|
|
|
|
|
|
1390
|
|
|
|
|
|
|
|
1391
|
|
|
|
|
|
|
=head2 register_varhandler ($event, $handler)
|
1392
|
|
|
|
|
|
|
|
1393
|
|
|
|
|
|
|
Registers a variable handler in the event context. If there is a handler registered for a name, it will be called instead of the normal
|
1394
|
|
|
|
|
|
|
hash read and write. This means you can attach active content to a variable, then treat it just like any other variable in your code.
|
1395
|
|
|
|
|
|
|
|
1396
|
|
|
|
|
|
|
=cut
|
1397
|
|
|
|
|
|
|
|
1398
|
|
|
|
|
|
|
sub register_varhandler {
|
1399
|
4
|
|
|
4
|
1
|
12
|
my ($self, $key, $handler) = @_;
|
1400
|
4
|
|
|
|
|
23
|
$self->{h}->{$key} = $handler;
|
1401
|
|
|
|
|
|
|
}
|
1402
|
|
|
|
|
|
|
|
1403
|
|
|
|
|
|
|
|
1404
|
|
|
|
|
|
|
=head2 subs()
|
1405
|
|
|
|
|
|
|
|
1406
|
|
|
|
|
|
|
Returns all our direct children named 'sub', plus the same thing from our parent. Our answers mask our parent's.
|
1407
|
|
|
|
|
|
|
|
1408
|
|
|
|
|
|
|
=cut
|
1409
|
|
|
|
|
|
|
|
1410
|
|
|
|
|
|
|
sub subs {
|
1411
|
40
|
|
|
40
|
1
|
70
|
my $self = shift;
|
1412
|
40
|
100
|
|
|
|
103
|
my $subs = $self->parent ? $self->parent()->subs() : {};
|
1413
|
40
|
|
|
|
|
163
|
foreach ($self->nodes()) {
|
1414
|
80
|
100
|
|
|
|
189
|
next unless $_->tag() eq 'sub';
|
1415
|
2
|
|
|
|
|
29
|
$_->build();
|
1416
|
2
|
|
|
|
|
13
|
$subs->{$_->name} = $_;
|
1417
|
|
|
|
|
|
|
}
|
1418
|
40
|
|
|
|
|
133
|
return $subs;
|
1419
|
|
|
|
|
|
|
}
|
1420
|
|
|
|
|
|
|
|
1421
|
|
|
|
|
|
|
=head2 find_filter(filter), call_filter(filter, value)
|
1422
|
|
|
|
|
|
|
|
1423
|
|
|
|
|
|
|
Finds a filter by name from a given point in the tree and calls it with a set of parameters.
|
1424
|
|
|
|
|
|
|
|
1425
|
|
|
|
|
|
|
=cut
|
1426
|
|
|
|
|
|
|
|
1427
|
|
|
|
|
|
|
sub find_filter {
|
1428
|
0
|
|
|
0
|
1
|
|
my ($self, $filter) = @_;
|
1429
|
|
|
|
|
|
|
|
1430
|
0
|
0
|
|
|
|
|
$self = $self->{instantiates} if $self->{instantiates}; # TODO: I think this is probably correct.
|
1431
|
0
|
|
|
|
|
|
foreach ($self->nodes()) {
|
1432
|
0
|
0
|
0
|
|
|
|
return $_ if $_->is("sub|filter") and $_->name eq $filter;
|
1433
|
|
|
|
|
|
|
}
|
1434
|
0
|
0
|
|
|
|
|
return $self->parent->find_filter($filter) if $self->parent;
|
1435
|
0
|
|
|
|
|
|
$filter = Decl->register_filter($filter);
|
1436
|
0
|
|
|
|
|
|
return $filter;
|
1437
|
|
|
|
|
|
|
}
|
1438
|
|
|
|
|
|
|
|
1439
|
|
|
|
|
|
|
sub call_filter {
|
1440
|
0
|
|
|
0
|
1
|
|
my $self = shift;
|
1441
|
0
|
|
|
|
|
|
my $filter = shift;
|
1442
|
0
|
|
|
|
|
|
my $value = shift;
|
1443
|
|
|
|
|
|
|
|
1444
|
0
|
|
|
|
|
|
$filter = $self->find_filter($filter);
|
1445
|
0
|
0
|
|
|
|
|
if (not defined $filter) {
|
1446
|
|
|
|
|
|
|
# TODO: warning
|
1447
|
0
|
|
|
|
|
|
return $value;
|
1448
|
|
|
|
|
|
|
}
|
1449
|
0
|
0
|
|
|
|
|
if (ref $filter eq 'CODE') {
|
1450
|
0
|
|
|
|
|
|
return &$filter ($value, @_);
|
1451
|
|
|
|
|
|
|
}
|
1452
|
0
|
|
|
|
|
|
$filter->go(undef, $value, @_);
|
1453
|
|
|
|
|
|
|
}
|
1454
|
|
|
|
|
|
|
|
1455
|
|
|
|
|
|
|
#=head2 AUTOLOAD
|
1456
|
|
|
|
|
|
|
#
|
1457
|
|
|
|
|
|
|
#If a call is made against a node with a payload, the node will try to proxy the payload object's methods using AUTOLOAD.
|
1458
|
|
|
|
|
|
|
#
|
1459
|
|
|
|
|
|
|
#TODO: some kind of dot notation to permit this to work with inner nodes.
|
1460
|
|
|
|
|
|
|
#
|
1461
|
|
|
|
|
|
|
#=cut
|
1462
|
|
|
|
|
|
|
#
|
1463
|
|
|
|
|
|
|
#sub AUTOLOAD {
|
1464
|
|
|
|
|
|
|
# my ($self) = @_;
|
1465
|
|
|
|
|
|
|
# croak "No method $AUTOLOAD" unless $self->payload and ref($self->payload);
|
1466
|
|
|
|
|
|
|
#
|
1467
|
|
|
|
|
|
|
# my $name = our $AUTOLOAD;
|
1468
|
|
|
|
|
|
|
# $name =~ s/.*://;
|
1469
|
|
|
|
|
|
|
# return "No method $AUTOLOAD" unless $self->payload->can($name);
|
1470
|
|
|
|
|
|
|
#
|
1471
|
|
|
|
|
|
|
# *$AUTOLOAD = eval "sub { my \$self = shift; \$self->payload->$name (\@_) }";
|
1472
|
|
|
|
|
|
|
# goto &$AUTOLOAD;
|
1473
|
|
|
|
|
|
|
#}
|
1474
|
|
|
|
|
|
|
|
1475
|
|
|
|
|
|
|
|
1476
|
|
|
|
|
|
|
=head1 OUTPUT
|
1477
|
|
|
|
|
|
|
|
1478
|
|
|
|
|
|
|
=head2 write(), log(), output()
|
1479
|
|
|
|
|
|
|
|
1480
|
|
|
|
|
|
|
The C function is supported for any node; by default it simply passes its arguments up to its parent. The top of the tree will print everything
|
1481
|
|
|
|
|
|
|
to STDOUT - by default. At any point in the tree, though, a node may claim ownership of the output stream by having an option [output]; any C
|
1482
|
|
|
|
|
|
|
called below that node's parent will be written to that node's C. Obviously, this is a good way to use files.
|
1483
|
|
|
|
|
|
|
|
1484
|
|
|
|
|
|
|
The C function is exactly the same, except the default is to write to STDERR and the option to use is [log].
|
1485
|
|
|
|
|
|
|
|
1486
|
|
|
|
|
|
|
There is another difference: a file used as [output] will by default start from scratch ('w'), while a file used as [log] will append its material ('a').
|
1487
|
|
|
|
|
|
|
Either is opened during build, and closed when the program closes.
|
1488
|
|
|
|
|
|
|
|
1489
|
|
|
|
|
|
|
If it's not in [output] or [log] mode, however, each call to C on a file is independent; the file is closed afterwards and no handle is kept around.
|
1490
|
|
|
|
|
|
|
This can be overridden with a (keepopen) parameter or a (>>) parameter for appending. (Any appending file will be opened for appending during build and
|
1491
|
|
|
|
|
|
|
closed when the program closes.)
|
1492
|
|
|
|
|
|
|
|
1493
|
|
|
|
|
|
|
If a file is in keepopen mode, the buffers are flushed after each C/C.
|
1494
|
|
|
|
|
|
|
|
1495
|
|
|
|
|
|
|
The C |
1496
|
|
|
|
|
|
|
|
1497
|
|
|
|
|
|
|
=cut
|
1498
|
|
|
|
|
|
|
|
1499
|
|
|
|
|
|
|
sub output {
|
1500
|
0
|
|
|
0
|
1
|
|
my $self = shift;
|
1501
|
0
|
|
|
|
|
|
$self->write(@_);
|
1502
|
|
|
|
|
|
|
}
|
1503
|
|
|
|
|
|
|
|
1504
|
|
|
|
|
|
|
sub write {
|
1505
|
0
|
|
|
0
|
1
|
|
my $self = shift;
|
1506
|
0
|
|
|
|
|
|
$self->parent->write(@_);
|
1507
|
|
|
|
|
|
|
}
|
1508
|
|
|
|
|
|
|
sub log {
|
1509
|
0
|
|
|
0
|
1
|
|
my $self = shift;
|
1510
|
0
|
|
|
|
|
|
$self->parent->log(@_);
|
1511
|
|
|
|
|
|
|
}
|
1512
|
|
|
|
|
|
|
|
1513
|
|
|
|
|
|
|
=head1 AUTHOR
|
1514
|
|
|
|
|
|
|
|
1515
|
|
|
|
|
|
|
Michael Roberts, C<< >>
|
1516
|
|
|
|
|
|
|
|
1517
|
|
|
|
|
|
|
=head1 BUGS
|
1518
|
|
|
|
|
|
|
|
1519
|
|
|
|
|
|
|
Please report any bugs or feature requests to C, or through
|
1520
|
|
|
|
|
|
|
the web interface at L. I will be notified, and then you'll
|
1521
|
|
|
|
|
|
|
automatically be notified of progress on your bug as I make changes.
|
1522
|
|
|
|
|
|
|
|
1523
|
|
|
|
|
|
|
=head1 LICENSE AND COPYRIGHT
|
1524
|
|
|
|
|
|
|
|
1525
|
|
|
|
|
|
|
Copyright 2010 Michael Roberts.
|
1526
|
|
|
|
|
|
|
|
1527
|
|
|
|
|
|
|
This program is free software; you can redistribute it and/or modify it
|
1528
|
|
|
|
|
|
|
under the terms of either: the GNU General Public License as published
|
1529
|
|
|
|
|
|
|
by the Free Software Foundation; or the Artistic License.
|
1530
|
|
|
|
|
|
|
|
1531
|
|
|
|
|
|
|
See http://dev.perl.org/licenses/ for more information.
|
1532
|
|
|
|
|
|
|
|
1533
|
|
|
|
|
|
|
=cut
|
1534
|
|
|
|
|
|
|
|
1535
|
|
|
|
|
|
|
1; # End of Decl::Node
|