line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
package Decl::Semantics::Code;
|
2
|
|
|
|
|
|
|
|
3
|
12
|
|
|
12
|
|
67
|
use warnings;
|
|
12
|
|
|
|
|
25
|
|
|
12
|
|
|
|
|
362
|
|
4
|
12
|
|
|
12
|
|
112
|
use strict;
|
|
12
|
|
|
|
|
25
|
|
|
12
|
|
|
|
|
345
|
|
5
|
|
|
|
|
|
|
|
6
|
12
|
|
|
12
|
|
62
|
use base qw(Decl::Node);
|
|
12
|
|
|
|
|
22
|
|
|
12
|
|
|
|
|
9290
|
|
7
|
12
|
|
|
12
|
|
192
|
use Decl::Util;
|
|
12
|
|
|
|
|
29
|
|
|
12
|
|
|
|
|
1372
|
|
8
|
12
|
|
|
12
|
|
79
|
use Data::Dumper;
|
|
12
|
|
|
|
|
27
|
|
|
12
|
|
|
|
|
42858
|
|
9
|
|
|
|
|
|
|
|
10
|
|
|
|
|
|
|
=head1 NAME
|
11
|
|
|
|
|
|
|
|
12
|
|
|
|
|
|
|
Decl::Semantics::Code - implements some code (perl or otherwise) in a declarative framework.
|
13
|
|
|
|
|
|
|
|
14
|
|
|
|
|
|
|
=head1 VERSION
|
15
|
|
|
|
|
|
|
|
16
|
|
|
|
|
|
|
Version 0.01
|
17
|
|
|
|
|
|
|
|
18
|
|
|
|
|
|
|
=cut
|
19
|
|
|
|
|
|
|
|
20
|
|
|
|
|
|
|
our $VERSION = '0.01';
|
21
|
|
|
|
|
|
|
|
22
|
|
|
|
|
|
|
|
23
|
|
|
|
|
|
|
=head1 SYNOPSIS
|
24
|
|
|
|
|
|
|
|
25
|
|
|
|
|
|
|
This class serves two purposes: first, it's an example of what a semantic node class should look like, and second, it
|
26
|
|
|
|
|
|
|
will probably end up being the class that builds most of the code references in a declarative program.
|
27
|
|
|
|
|
|
|
|
28
|
|
|
|
|
|
|
=head2 defines(), tags_defined()
|
29
|
|
|
|
|
|
|
|
30
|
|
|
|
|
|
|
Called by Decl::Semantics during import, to find out what tags this plugin claims to implement and the
|
31
|
|
|
|
|
|
|
parsers used to build its content.
|
32
|
|
|
|
|
|
|
|
33
|
|
|
|
|
|
|
=cut
|
34
|
0
|
|
|
0
|
1
|
0
|
sub defines { ('on', 'do', 'perl', 'sub'); }
|
35
|
|
|
|
|
|
|
our %build_handlers = ( perl => { node => sub { Decl::Semantics::Code->new (@_) }, body => 'none' } );
|
36
|
12
|
|
|
12
|
1
|
80
|
sub tags_defined { Decl->new_data(<
|
37
|
|
|
|
|
|
|
on
|
38
|
|
|
|
|
|
|
do
|
39
|
|
|
|
|
|
|
sub
|
40
|
|
|
|
|
|
|
filter
|
41
|
|
|
|
|
|
|
perl (body=text)
|
42
|
|
|
|
|
|
|
EOF
|
43
|
|
|
|
|
|
|
|
44
|
|
|
|
|
|
|
=head2 build_payload, build_macro_payload, fixvars, fixcall, fixevent, fixfind, make_code, make_macro_code, parse_select, make_select, make_dml, make_output, make_ifnew
|
45
|
|
|
|
|
|
|
|
46
|
|
|
|
|
|
|
The C function is then called when this object's payload is built (i.e. in the stage when we're adding semantics to our
|
47
|
|
|
|
|
|
|
parsed syntax). The payload of a code object is its callable code result.
|
48
|
|
|
|
|
|
|
|
49
|
|
|
|
|
|
|
The C function builds code in an event context; it actually calls C, which does the same in an arbitrary
|
50
|
|
|
|
|
|
|
node context that you supply (but that defaults to the event context of the code node).
|
51
|
|
|
|
|
|
|
|
52
|
|
|
|
|
|
|
The parent's payload will always have been created by the time this function is called.
|
53
|
|
|
|
|
|
|
|
54
|
|
|
|
|
|
|
The C function is by far the most complex of our code generators, as it has to find an iterator source and build a while loop,
|
55
|
|
|
|
|
|
|
or a DBI database and build a query and select loop. The parsing is split out into C in order to make it usable from elsewhere.
|
56
|
|
|
|
|
|
|
|
57
|
|
|
|
|
|
|
The C function handles the non-select DBI keywords (just 'insert', 'update', and 'delete').
|
58
|
|
|
|
|
|
|
|
59
|
|
|
|
|
|
|
The C functions munge various things around in our code generation scheme.
|
60
|
|
|
|
|
|
|
|
61
|
|
|
|
|
|
|
The C function handles text blocks for output delineated with "<<".
|
62
|
|
|
|
|
|
|
|
63
|
|
|
|
|
|
|
The C is probably going overboard with specific select tweaks; I really need to start thinking harder about real macros in the code.
|
64
|
|
|
|
|
|
|
|
65
|
|
|
|
|
|
|
=cut
|
66
|
|
|
|
|
|
|
|
67
|
17
|
|
|
17
|
1
|
123
|
sub fixvars { '$self->{v}->{\'' . $_[0] . '\'}' }
|
68
|
|
|
|
|
|
|
sub fixcall {
|
69
|
4
|
0
|
33
|
4
|
1
|
37
|
return '$self->' . $_[0] if ($_[0] eq 'output' ||
|
|
|
|
33
|
|
|
|
|
70
|
|
|
|
|
|
|
$_[0] eq 'write' ||
|
71
|
|
|
|
|
|
|
$_[0] eq 'log');
|
72
|
0
|
|
|
|
|
0
|
'$cx->' . $_[0]
|
73
|
|
|
|
|
|
|
}
|
74
|
2
|
|
|
2
|
1
|
13
|
sub fixevent { '$cx->do(\'' . $_[0] . '\')' }
|
75
|
|
|
|
|
|
|
|
76
|
0
|
|
|
0
|
1
|
0
|
sub fixfind { '$self->find_context(' . $_[0] . ')' }
|
77
|
|
|
|
|
|
|
|
78
|
|
|
|
|
|
|
our $next_counter = 1;
|
79
|
|
|
|
|
|
|
|
80
|
|
|
|
|
|
|
sub parse_select { # 2011-08-27 - factored out of make_select below - made possible once more by the magic of unit testing!
|
81
|
2
|
|
|
2
|
1
|
4
|
my ($foreach) = @_;
|
82
|
2
|
|
|
|
|
4
|
my @vars = ();
|
83
|
2
|
|
|
|
|
3
|
my $keyword = '';
|
84
|
|
|
|
|
|
|
|
85
|
2
|
100
|
|
|
|
14
|
if ($foreach =~ /^\s*(.*?)\s+in\s+(.*?)\s*$/) {
|
86
|
1
|
|
|
|
|
4
|
my ($target, $source) = ($1, $2);
|
87
|
1
|
|
|
|
|
6
|
@vars = split /\s*[, ]\s*/, $target;
|
88
|
1
|
|
|
|
|
8
|
return ('foreach', $target, $source, @vars);
|
89
|
|
|
|
|
|
|
}
|
90
|
|
|
|
|
|
|
|
91
|
1
|
50
|
|
|
|
12
|
if ($foreach =~ /^\s*(.*?)\s+from\s+(.*?)\s*$/) {
|
92
|
0
|
|
|
|
|
0
|
my ($target, $source) = ($1, $2);
|
93
|
0
|
|
|
|
|
0
|
my $t = $target;
|
94
|
0
|
|
|
|
|
0
|
$t =~ s/^(distinct|all)\s+//;
|
95
|
0
|
|
|
|
|
0
|
@vars = map { s/^.* //; $_ } split (/\s*,\s*/, $t);
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
96
|
0
|
|
|
|
|
0
|
return ('select', $target, $source, @vars)
|
97
|
|
|
|
|
|
|
}
|
98
|
1
|
50
|
|
|
|
5
|
if ($foreach !~ /\s/) {
|
99
|
1
|
|
|
|
|
7
|
return ('foreach', '', $foreach);
|
100
|
|
|
|
|
|
|
}
|
101
|
|
|
|
|
|
|
|
102
|
0
|
|
|
|
|
0
|
return ('error');
|
103
|
|
|
|
|
|
|
}
|
104
|
|
|
|
|
|
|
|
105
|
|
|
|
|
|
|
sub make_select {
|
106
|
2
|
|
|
2
|
1
|
6
|
my ($self, $foreach, $keyword) = @_;
|
107
|
2
|
|
|
|
|
8
|
my $cx = $self->event_context;
|
108
|
|
|
|
|
|
|
|
109
|
2
|
|
|
|
|
5
|
my ($target, $source);
|
110
|
2
|
|
|
|
|
4
|
my @vars = ();
|
111
|
2
|
|
|
|
|
4
|
my @last_vars = ();
|
112
|
|
|
|
|
|
|
|
113
|
2
|
|
|
|
|
6
|
($keyword, $target, $source, @vars) = parse_select($foreach);
|
114
|
2
|
|
|
|
|
5
|
@last_vars = map { '_last_' . $_ . '_value' } @vars;
|
|
2
|
|
|
|
|
7
|
|
115
|
|
|
|
|
|
|
|
116
|
2
|
50
|
|
|
|
13
|
if ($keyword eq 'error') {
|
117
|
0
|
|
|
|
|
0
|
$self->error("'^foreach/select $foreach' can't be parsed");
|
118
|
0
|
|
|
|
|
0
|
return 'if (0) {';
|
119
|
|
|
|
|
|
|
}
|
120
|
|
|
|
|
|
|
|
121
|
2
|
|
|
|
|
3
|
my $unique = $next_counter++;
|
122
|
2
|
|
|
|
|
2
|
my $ret;
|
123
|
|
|
|
|
|
|
|
124
|
2
|
50
|
|
|
|
6
|
if ($keyword eq 'foreach') { # Normal data
|
125
|
2
|
|
|
|
|
10
|
my ($datasource, $type) = $self->find_data($source); # TODO: error handling if source not found.
|
126
|
|
|
|
|
|
|
|
127
|
2
|
100
|
66
|
|
|
18
|
if (not $target and $datasource->is ('data')) {
|
128
|
|
|
|
|
|
|
# Take target from definition of data source.
|
129
|
1
|
|
|
|
|
6
|
push @vars, $datasource->parmlist;
|
130
|
1
|
|
|
|
|
5
|
push @last_vars, map { '_last_' . $_ . '_value' } $datasource->parmlist;
|
|
2
|
|
|
|
|
5
|
|
131
|
|
|
|
|
|
|
}
|
132
|
|
|
|
|
|
|
|
133
|
2
|
50
|
|
|
|
10
|
if ($type eq 'text') {
|
|
|
50
|
|
|
|
|
|
134
|
0
|
|
|
|
|
0
|
my $my = '';
|
135
|
0
|
0
|
|
|
|
0
|
if (@vars) {
|
136
|
0
|
|
|
|
|
0
|
$target = 'my $' . shift @vars;
|
137
|
0
|
0
|
|
|
|
0
|
$my = 'my ($' . join (', $', @vars) . '); ' if @vars;
|
138
|
0
|
0
|
|
|
|
0
|
$my .= 'my ($' . join (', $', @last_vars) . '); ' if @last_vars;
|
139
|
|
|
|
|
|
|
} else {
|
140
|
0
|
|
|
|
|
0
|
$target = '$_';
|
141
|
|
|
|
|
|
|
}
|
142
|
0
|
|
|
|
|
0
|
$ret .= '{ ';
|
143
|
0
|
|
|
|
|
0
|
$ret .= 'my @text_node = $self->find_data(\'' . $source . '\'); ';
|
144
|
0
|
|
|
|
|
0
|
$ret .= 'my $iterator = $text_node[0]->iterate; ';
|
145
|
0
|
|
|
|
|
0
|
$ret .= 'while (' . $target . ' = $iterator->next) { ';
|
146
|
0
|
|
|
|
|
0
|
$ret .= $my;
|
147
|
|
|
|
|
|
|
} elsif ($type eq 'data') {
|
148
|
2
|
|
|
|
|
4
|
$ret .= '{ ';
|
149
|
2
|
|
|
|
|
5
|
$ret .= 'my @data_node = $self->find_data(\'' . $source . '\'); ';
|
150
|
2
|
|
|
|
|
4
|
$ret .= 'my $iterator = $data_node[0]->iterate; ';
|
151
|
2
|
|
|
|
|
3
|
$ret .= 'while (my $line = $iterator->next) { ';
|
152
|
2
|
|
|
|
|
6
|
$ret .= 'my ($' . join (', $', @vars) . ') = @$line;';
|
153
|
2
|
|
|
|
|
14
|
$ret .= 'my ($' . join (', $', @last_vars) . ') = @$line;';
|
154
|
|
|
|
|
|
|
} else {
|
155
|
0
|
|
|
|
|
0
|
$self->error ("node foreach not implemented yet");
|
156
|
0
|
|
|
|
|
0
|
$ret = 'if (0) {';
|
157
|
|
|
|
|
|
|
}
|
158
|
|
|
|
|
|
|
} else { # Database (e.g. DBI) select
|
159
|
|
|
|
|
|
|
# This is kind of the default mode for DBI; absent specification to the contrary, we find the first database handle and use it.
|
160
|
|
|
|
|
|
|
# But "source" is where everything is coming from, so if we can munge in in some way, this is where that will happen.
|
161
|
|
|
|
|
|
|
|
162
|
0
|
0
|
|
|
|
0
|
if ($vars[0] eq '*') {
|
163
|
0
|
|
|
|
|
0
|
$ret .= '{ ';
|
164
|
0
|
|
|
|
|
0
|
$ret .= 'my $dbh = $self->find_context(\'database\')->payload; ';
|
165
|
0
|
|
|
|
|
0
|
$ret .= 'my $sth = $dbh->prepare ("select ' . $target . ' from ' . $source . '"); ';
|
166
|
0
|
|
|
|
|
0
|
$ret .= '$sth->execute(); ';
|
167
|
0
|
|
|
|
|
0
|
$ret .= 'while (my $row = $sth->fetchrow_hashref()) {';
|
168
|
|
|
|
|
|
|
} else {
|
169
|
0
|
|
|
|
|
0
|
$ret .= '{ ';
|
170
|
0
|
|
|
|
|
0
|
$ret .= 'my $dbh = $self->find_context(\'database\')->payload; ';
|
171
|
0
|
|
|
|
|
0
|
$ret .= 'my $sth = $dbh->prepare ("select ' . $target . ' from ' . $source . '"); ';
|
172
|
0
|
|
|
|
|
0
|
$ret .= '$sth->execute(); ';
|
173
|
0
|
|
|
|
|
0
|
$ret .= 'my ($' . join (', $', @vars) . '); ';
|
174
|
0
|
|
|
|
|
0
|
$ret .= 'my ($' . join (', $', @last_vars) . '); ';
|
175
|
0
|
|
|
|
|
0
|
$ret .= '$sth->bind_columns (\$'. join (', \$', @vars) . '); ';
|
176
|
0
|
|
|
|
|
0
|
$ret .= 'while ($sth->fetch()) {';
|
177
|
|
|
|
|
|
|
}
|
178
|
|
|
|
|
|
|
}
|
179
|
|
|
|
|
|
|
|
180
|
2
|
|
|
|
|
15
|
$ret;
|
181
|
|
|
|
|
|
|
}
|
182
|
|
|
|
|
|
|
|
183
|
|
|
|
|
|
|
sub make_dml {
|
184
|
0
|
|
|
0
|
1
|
0
|
my ($self, $foreach, $keyword) = @_;
|
185
|
0
|
|
|
|
|
0
|
my $cx = $self->event_context;
|
186
|
|
|
|
|
|
|
}
|
187
|
|
|
|
|
|
|
|
188
|
|
|
|
|
|
|
sub make_output {
|
189
|
1
|
|
|
1
|
1
|
2
|
my ($output, $flag) = @_;
|
190
|
|
|
|
|
|
|
|
191
|
1
|
|
|
|
|
2
|
my $r;
|
192
|
1
|
50
|
|
|
|
4
|
if ($flag eq '"') {
|
193
|
0
|
|
|
|
|
0
|
$r = '$self->output(<<"EOF");' . "\n";
|
194
|
|
|
|
|
|
|
} else {
|
195
|
1
|
|
|
|
|
2
|
$r = '$self->output($Decl::template_engine->express(<<\'EOF\', $cx));' . "\n";
|
196
|
|
|
|
|
|
|
}
|
197
|
1
|
|
|
|
|
3
|
$r .= $output;
|
198
|
1
|
|
|
|
|
1
|
$r .= "EOF\n";
|
199
|
1
|
|
|
|
|
3
|
return $r;
|
200
|
|
|
|
|
|
|
}
|
201
|
|
|
|
|
|
|
|
202
|
|
|
|
|
|
|
sub make_ifnew {
|
203
|
0
|
|
|
0
|
1
|
0
|
my ($v) = @_;
|
204
|
0
|
|
|
|
|
0
|
return 'if (not defined $_last_' . $v . '_value or $_last_' . $v . '_value ne $' . $v . ') {' . "\n" .
|
205
|
|
|
|
|
|
|
' $_last_' . $v . '_value = $' . $v . ';' . "\n";
|
206
|
|
|
|
|
|
|
}
|
207
|
|
|
|
|
|
|
|
208
|
|
|
|
|
|
|
sub make_code {
|
209
|
1
|
|
|
1
|
1
|
3
|
my $self = shift;
|
210
|
1
|
|
|
|
|
3
|
my $code = shift;
|
211
|
|
|
|
|
|
|
|
212
|
1
|
|
|
|
|
6
|
make_macro_code($self, $code, undef, @_);
|
213
|
|
|
|
|
|
|
}
|
214
|
|
|
|
|
|
|
|
215
|
|
|
|
|
|
|
sub make_macro_code {
|
216
|
20
|
|
|
20
|
1
|
55
|
my $self = shift;
|
217
|
20
|
|
|
|
|
43
|
my $code = shift;
|
218
|
20
|
|
66
|
|
|
93
|
my $outer_cx = shift || $self->event_context;
|
219
|
|
|
|
|
|
|
|
220
|
20
|
|
|
|
|
162
|
my $sem = $outer_cx->semantics;
|
221
|
20
|
|
|
|
|
153
|
my $subs = $self->subs();
|
222
|
|
|
|
|
|
|
|
223
|
20
|
|
|
|
|
56
|
my $preamble = 'my $cx = shift || $outer_cx;' . "\n";
|
224
|
20
|
100
|
|
|
|
85
|
if (@_) {
|
225
|
5
|
|
|
|
|
29
|
$preamble .= 'my ($' . join (', $', @_) . ') = @_;' . "\n\n"; # I love generating code.
|
226
|
|
|
|
|
|
|
}
|
227
|
20
|
|
|
|
|
95
|
foreach my $subname (keys %$subs) {
|
228
|
2
|
|
|
|
|
13
|
$preamble .= 'local *' . $subname . ' = $subs->{\'' . $subname . '\'}->{sub};' . "\n";
|
229
|
|
|
|
|
|
|
}
|
230
|
20
|
|
|
|
|
63
|
$code = $preamble . $code;
|
231
|
20
|
|
|
|
|
80
|
$code =~ s/\^db( *)->/\$self->find_context('database')->dbh->/g;
|
232
|
20
|
|
|
|
|
135
|
$code =~ s/\$\^(\w+)/fixvars($1)/ge;
|
|
17
|
|
|
|
|
65
|
|
233
|
20
|
|
|
|
|
75
|
$code =~ s/\^!(\w+)/fixevent($1)/ge;
|
|
2
|
|
|
|
|
9
|
|
234
|
20
|
|
|
|
|
53
|
$code =~ s/\^\((.*?)\)/fixfind($1)/ge; # TODO: balanced parens would be a lot more convincing in that regexp...
|
|
0
|
|
|
|
|
0
|
|
235
|
20
|
|
|
|
|
70
|
$code =~ s/\^foreach (.*) {{/$self->make_select($1, 'foreach')/ge;
|
|
2
|
|
|
|
|
9
|
|
236
|
20
|
|
|
|
|
56
|
$code =~ s/\^select (.*) {{/$self->make_select($1, 'select')/ge;
|
|
0
|
|
|
|
|
0
|
|
237
|
20
|
|
|
|
|
53
|
$code =~ s/\^if-new (.*) {/make_ifnew($1)/ge;
|
|
0
|
|
|
|
|
0
|
|
238
|
20
|
|
|
|
|
53
|
$code =~ s/\^(insert .*);/$self->make_dml($1)/ge;
|
|
0
|
|
|
|
|
0
|
|
239
|
20
|
|
|
|
|
52
|
$code =~ s/\^(delete .*);/$self->make_dml($1)/ge;
|
|
0
|
|
|
|
|
0
|
|
240
|
20
|
|
|
|
|
54
|
$code =~ s/\^(update .*);/$self->make_dml($1)/ge;
|
|
0
|
|
|
|
|
0
|
|
241
|
20
|
|
|
|
|
68
|
$code =~ s/\^(\w+)/fixcall($1)/ge;
|
|
4
|
|
|
|
|
12
|
|
242
|
|
|
|
|
|
|
|
243
|
20
|
|
|
|
|
46
|
my $lcode = '';
|
244
|
20
|
|
|
|
|
48
|
my $mode = 0;
|
245
|
20
|
|
|
|
|
38
|
my $indent = 0;
|
246
|
20
|
|
|
|
|
31
|
my $output;
|
247
|
20
|
|
|
|
|
41
|
my $flag = '';
|
248
|
20
|
|
|
|
|
99
|
foreach my $line (split /\n/, $code) {
|
249
|
86
|
100
|
|
|
|
184
|
if ($mode) {
|
250
|
2
|
|
|
|
|
4
|
my $leader = substr($line, 0, $indent);
|
251
|
2
|
100
|
|
|
|
9
|
if ($leader =~ /^[\s<]*$/) {
|
252
|
1
|
|
|
|
|
4
|
$output .= substr($line, $indent) . "\n";
|
253
|
|
|
|
|
|
|
} else {
|
254
|
1
|
|
|
|
|
5
|
$lcode .= make_output($output, $flag);
|
255
|
1
|
|
|
|
|
2
|
$mode = 0;
|
256
|
1
|
|
|
|
|
4
|
$lcode .= $line . "\n";
|
257
|
|
|
|
|
|
|
}
|
258
|
|
|
|
|
|
|
} else {
|
259
|
84
|
100
|
|
|
|
204
|
if ($line =~ /^\s*<) {
|
260
|
1
|
|
|
|
|
2
|
my $olen = length($line);
|
261
|
1
|
|
|
|
|
6
|
$line =~ s/^\s*</;
|
262
|
1
|
|
|
|
|
3
|
$flag = substr($line, 0, 1);
|
263
|
1
|
|
|
|
|
13
|
$line =~ s/$flag\s*//;
|
264
|
1
|
|
|
|
|
3
|
$indent = $olen - length ($line);
|
265
|
1
|
|
|
|
|
2
|
$output = $line . "\n";
|
266
|
1
|
|
|
|
|
3
|
$mode = 1;
|
267
|
|
|
|
|
|
|
} else {
|
268
|
83
|
|
|
|
|
236
|
$lcode .= $line . "\n";
|
269
|
|
|
|
|
|
|
}
|
270
|
|
|
|
|
|
|
}
|
271
|
|
|
|
|
|
|
}
|
272
|
20
|
50
|
|
|
|
85
|
if ($mode) {
|
273
|
0
|
|
|
|
|
0
|
$lcode .= make_output($output, $flag);
|
274
|
|
|
|
|
|
|
}
|
275
|
|
|
|
|
|
|
|
276
|
20
|
|
|
|
|
3358
|
my $sub = eval "sub {" . $lcode . "\n}";
|
277
|
20
|
50
|
|
|
|
94
|
$self->error ($@) if $@; # TODO: man, this is just the wrong way to do this.
|
278
|
20
|
50
|
|
|
|
70
|
print STDERR $@ if $@;
|
279
|
|
|
|
|
|
|
|
280
|
20
|
100
|
|
|
|
70
|
if (wantarray) {
|
281
|
19
|
|
|
|
|
155
|
return ($sub, $lcode);
|
282
|
|
|
|
|
|
|
} else {
|
283
|
1
|
|
|
|
|
10
|
return $sub;
|
284
|
|
|
|
|
|
|
}
|
285
|
|
|
|
|
|
|
}
|
286
|
|
|
|
|
|
|
|
287
|
|
|
|
|
|
|
sub build_payload { # TODO: split this out into build_code and build_payload
|
288
|
25
|
|
|
25
|
1
|
58
|
my $self = shift;
|
289
|
25
|
|
|
|
|
49
|
my $is_event = shift; # @_ is now the list of 'my' variables the code expects, by name.
|
290
|
25
|
|
|
|
|
108
|
build_macro_payload($self, $is_event, undef, @_);
|
291
|
25
|
100
|
|
|
|
96
|
$self->{callable} = 'sub' if $self->is('sub');
|
292
|
25
|
|
|
|
|
63
|
$self;
|
293
|
|
|
|
|
|
|
}
|
294
|
|
|
|
|
|
|
|
295
|
|
|
|
|
|
|
sub build_macro_payload {
|
296
|
28
|
|
|
28
|
1
|
57
|
my $self = shift;
|
297
|
28
|
|
|
|
|
55
|
my $is_event = shift;
|
298
|
28
|
|
33
|
|
|
242
|
my $cx = shift || $self->event_context;
|
299
|
|
|
|
|
|
|
|
300
|
28
|
100
|
|
|
|
146
|
return $self if $self->{built};
|
301
|
27
|
|
|
|
|
84
|
$self->{built} = 1;
|
302
|
|
|
|
|
|
|
|
303
|
27
|
100
|
|
|
|
104
|
if (!@_) { # Didn't get any 'my' variables explicitly defined.
|
304
|
15
|
|
|
|
|
107
|
@_ = $self->optionlist;
|
305
|
|
|
|
|
|
|
}
|
306
|
|
|
|
|
|
|
|
307
|
|
|
|
|
|
|
# Here's the tricky part. We have to build some code and evaluate it when asked. This could get arbitrarily complex.
|
308
|
|
|
|
|
|
|
# If we have a code body, that's our code. If we have both a body and a "code" (i.e. a one-line bracketed body), then
|
309
|
|
|
|
|
|
|
# the "code" takes precedence (e.g. Wx toolbars).
|
310
|
27
|
50
|
|
|
|
218
|
if ($self->code) { # TODO: this wasn't covered by the unit tests!
|
|
|
100
|
|
|
|
|
|
311
|
0
|
|
|
|
|
0
|
my $code = $self->code;
|
312
|
0
|
|
|
|
|
0
|
$code =~ s/^{//;
|
313
|
0
|
|
|
|
|
0
|
$code =~ s/}$//;
|
314
|
|
|
|
|
|
|
#print "code is $code\n";
|
315
|
0
|
|
|
|
|
0
|
($self->{sub}, $self->{gencode}) = make_macro_code ($self, $code, $cx, @_);
|
316
|
|
|
|
|
|
|
#print STDERR "1gencode is " . $self->{gencode} . "\n";
|
317
|
0
|
|
|
|
|
0
|
$self->{callable} = 1;
|
318
|
0
|
|
|
|
|
0
|
$self->{owncode} = 1;
|
319
|
|
|
|
|
|
|
} elsif ($self->body) {
|
320
|
19
|
|
|
|
|
88
|
($self->{sub}, $self->{gencode}) = make_macro_code ($self, $self->body, $cx, @_);
|
321
|
|
|
|
|
|
|
#print STDERR "gencode is " . $self->{gencode} . "\n";
|
322
|
|
|
|
|
|
|
|
323
|
19
|
|
|
|
|
113
|
$self->{callable} = 1;
|
324
|
19
|
|
|
|
|
48
|
$self->{owncode} = 1;
|
325
|
|
|
|
|
|
|
} else {
|
326
|
|
|
|
|
|
|
# No body means we're just going to build each of our children, and try to execute each of them in sequence when called.
|
327
|
|
|
|
|
|
|
# No body and no callable children means we're not callable either.
|
328
|
|
|
|
|
|
|
#print "making child-based caller:" . $self->myline . "\n";
|
329
|
8
|
|
|
|
|
19
|
my $child_code = 0;
|
330
|
8
|
|
|
|
|
48
|
foreach ($self->nodes) {
|
331
|
0
|
|
|
|
|
0
|
$_->build;
|
332
|
0
|
|
0
|
|
|
0
|
$child_code = $child_code || $_->{callable};
|
333
|
|
|
|
|
|
|
}
|
334
|
|
|
|
|
|
|
|
335
|
8
|
50
|
|
|
|
47
|
$self->{callable} = $child_code ? 1 : 0;
|
336
|
8
|
|
|
0
|
|
65
|
$self->{sub} = sub { $self->go(); };
|
|
0
|
|
|
|
|
0
|
|
337
|
8
|
|
|
|
|
48
|
$self->{owncode} = 0;
|
338
|
|
|
|
|
|
|
}
|
339
|
|
|
|
|
|
|
|
340
|
27
|
100
|
|
|
|
181
|
$self->{event} = $self->is ('on') ? 1 : 0;
|
341
|
27
|
100
|
33
|
|
|
208
|
if ($self->{callable} && ($is_event || ($self->is ('on') and $self->name))) {
|
|
|
|
66
|
|
|
|
|
342
|
4
|
|
|
|
|
15
|
$cx->register_event ($self->name, $self->{sub});
|
343
|
|
|
|
|
|
|
}
|
344
|
|
|
|
|
|
|
|
345
|
27
|
50
|
|
|
|
142
|
$self->{payload} = $self->{sub} unless $self->{payload}; # TODO: this seems fishy.
|
346
|
27
|
|
|
|
|
79
|
return $self;
|
347
|
|
|
|
|
|
|
}
|
348
|
|
|
|
|
|
|
|
349
|
|
|
|
|
|
|
|
350
|
|
|
|
|
|
|
=head1 AUTHOR
|
351
|
|
|
|
|
|
|
|
352
|
|
|
|
|
|
|
Michael Roberts, C<< >>
|
353
|
|
|
|
|
|
|
|
354
|
|
|
|
|
|
|
=head1 BUGS
|
355
|
|
|
|
|
|
|
|
356
|
|
|
|
|
|
|
Please report any bugs or feature requests to C, or through
|
357
|
|
|
|
|
|
|
the web interface at L. I will be notified, and then you'll
|
358
|
|
|
|
|
|
|
automatically be notified of progress on your bug as I make changes.
|
359
|
|
|
|
|
|
|
|
360
|
|
|
|
|
|
|
=head1 LICENSE AND COPYRIGHT
|
361
|
|
|
|
|
|
|
|
362
|
|
|
|
|
|
|
Copyright 2010 Michael Roberts.
|
363
|
|
|
|
|
|
|
|
364
|
|
|
|
|
|
|
This program is free software; you can redistribute it and/or modify it
|
365
|
|
|
|
|
|
|
under the terms of either: the GNU General Public License as published
|
366
|
|
|
|
|
|
|
by the Free Software Foundation; or the Artistic License.
|
367
|
|
|
|
|
|
|
|
368
|
|
|
|
|
|
|
See http://dev.perl.org/licenses/ for more information.
|
369
|
|
|
|
|
|
|
|
370
|
|
|
|
|
|
|
=cut
|
371
|
|
|
|
|
|
|
|
372
|
|
|
|
|
|
|
1; # End of Decl::Semantics::Code
|