| line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
|
1
|
|
|
|
|
|
|
# This code is part of Perl distribution Math-Formula version 0.18. |
|
2
|
|
|
|
|
|
|
# The POD got stripped from this file by OODoc version 3.03. |
|
3
|
|
|
|
|
|
|
# For contributors see file ChangeLog. |
|
4
|
|
|
|
|
|
|
|
|
5
|
|
|
|
|
|
|
# This software is copyright (c) 2023-2025 by Mark Overmeer. |
|
6
|
|
|
|
|
|
|
|
|
7
|
|
|
|
|
|
|
# This is free software; you can redistribute it and/or modify it under |
|
8
|
|
|
|
|
|
|
# the same terms as the Perl 5 programming language system itself. |
|
9
|
|
|
|
|
|
|
# SPDX-License-Identifier: Artistic-1.0-Perl OR GPL-1.0-or-later |
|
10
|
|
|
|
|
|
|
|
|
11
|
|
|
|
|
|
|
#oodist: *** DO NOT USE THIS VERSION FOR PRODUCTION *** |
|
12
|
|
|
|
|
|
|
#oodist: This file contains OODoc-style documentation which will get stripped |
|
13
|
|
|
|
|
|
|
#oodist: during its release in the distribution. You can use this file for |
|
14
|
|
|
|
|
|
|
#oodist: testing, however the code of this development version may be broken! |
|
15
|
|
|
|
|
|
|
|
|
16
|
|
|
|
|
|
|
package Math::Formula;{ |
|
17
|
|
|
|
|
|
|
our $VERSION = '0.18'; |
|
18
|
|
|
|
|
|
|
} |
|
19
|
|
|
|
|
|
|
|
|
20
|
|
|
|
|
|
|
|
|
21
|
28
|
|
|
28
|
|
1962934
|
use warnings; |
|
|
28
|
|
|
|
|
57
|
|
|
|
28
|
|
|
|
|
1766
|
|
|
22
|
28
|
|
|
28
|
|
306
|
use strict; |
|
|
28
|
|
|
|
|
66
|
|
|
|
28
|
|
|
|
|
779
|
|
|
23
|
28
|
|
|
28
|
|
137
|
use utf8; |
|
|
28
|
|
|
|
|
56
|
|
|
|
28
|
|
|
|
|
139
|
|
|
24
|
|
|
|
|
|
|
|
|
25
|
28
|
|
|
28
|
|
17438
|
use Log::Report 'math-formula'; |
|
|
28
|
|
|
|
|
3373377
|
|
|
|
28
|
|
|
|
|
142
|
|
|
26
|
|
|
|
|
|
|
|
|
27
|
28
|
|
|
28
|
|
24812
|
use Math::Formula::Token (); |
|
|
28
|
|
|
|
|
105
|
|
|
|
28
|
|
|
|
|
1001
|
|
|
28
|
28
|
|
|
28
|
|
17203
|
use Math::Formula::Type (); |
|
|
28
|
|
|
|
|
187
|
|
|
|
28
|
|
|
|
|
1490
|
|
|
29
|
|
|
|
|
|
|
|
|
30
|
28
|
|
|
28
|
|
306
|
use Scalar::Util qw/blessed/; |
|
|
28
|
|
|
|
|
93
|
|
|
|
28
|
|
|
|
|
22194
|
|
|
31
|
|
|
|
|
|
|
|
|
32
|
|
|
|
|
|
|
#-------------------- |
|
33
|
|
|
|
|
|
|
|
|
34
|
|
|
|
|
|
|
#-------------------- |
|
35
|
|
|
|
|
|
|
|
|
36
|
|
|
|
|
|
|
sub new(%) |
|
37
|
104
|
|
|
104
|
1
|
6826263
|
{ my ($class, $name, $expr, %self) = @_; |
|
38
|
104
|
|
|
|
|
298
|
$self{_name} = $name; |
|
39
|
104
|
|
|
|
|
305
|
$self{_expr} = $expr; |
|
40
|
104
|
|
|
|
|
1171
|
(bless {}, $class)->init(\%self); |
|
41
|
|
|
|
|
|
|
} |
|
42
|
|
|
|
|
|
|
|
|
43
|
|
|
|
|
|
|
sub init($) |
|
44
|
104
|
|
|
104
|
0
|
247
|
{ my ($self, $args) = @_; |
|
45
|
104
|
50
|
|
|
|
535
|
my $name = $self->{MSBE_name} = $args->{_name} or panic "every formular requires a name"; |
|
46
|
104
|
50
|
|
|
|
319
|
my $expr = $args->{_expr} or panic "every formular requires an expression"; |
|
47
|
104
|
|
|
|
|
279
|
my $returns = $self->{MSBE_returns} = $args->{returns}; |
|
48
|
|
|
|
|
|
|
|
|
49
|
104
|
100
|
100
|
|
|
751
|
if(ref $expr eq 'SCALAR') |
|
|
|
50
|
66
|
|
|
|
|
|
50
|
3
|
|
|
|
|
16
|
{ $expr = MF::STRING->new(undef, $$expr); |
|
51
|
|
|
|
|
|
|
} |
|
52
|
|
|
|
|
|
|
elsif(! ref $expr && $returns && $returns->isa('MF::STRING')) |
|
53
|
0
|
|
|
|
|
0
|
{ $expr = MF::STRING->new(undef, $expr); |
|
54
|
|
|
|
|
|
|
} |
|
55
|
|
|
|
|
|
|
|
|
56
|
104
|
|
|
|
|
246
|
$self->{MSBE_expr} = $expr; |
|
57
|
104
|
|
|
|
|
603
|
$self; |
|
58
|
|
|
|
|
|
|
} |
|
59
|
|
|
|
|
|
|
|
|
60
|
|
|
|
|
|
|
#-------------------- |
|
61
|
|
|
|
|
|
|
|
|
62
|
23
|
|
|
23
|
1
|
6495
|
sub name() { $_[0]->{MSBE_name} } |
|
63
|
306
|
|
|
306
|
1
|
1241
|
sub expression() { $_[0]->{MSBE_expr} } |
|
64
|
282
|
|
|
282
|
1
|
991
|
sub returns() { $_[0]->{MSBE_returns} } |
|
65
|
|
|
|
|
|
|
|
|
66
|
|
|
|
|
|
|
|
|
67
|
|
|
|
|
|
|
sub tree($) |
|
68
|
268
|
|
|
268
|
1
|
621
|
{ my ($self, $expression) = @_; |
|
69
|
268
|
|
66
|
|
|
1407
|
$self->{MSBE_ast} ||= $self->_build_ast($self->_tokenize($expression), 0); |
|
70
|
|
|
|
|
|
|
} |
|
71
|
|
|
|
|
|
|
|
|
72
|
|
|
|
|
|
|
# For testing only: to load a new expression without the need to create |
|
73
|
|
|
|
|
|
|
# a new object. |
|
74
|
|
|
|
|
|
|
sub _test($$) |
|
75
|
214
|
|
|
214
|
|
127637
|
{ my ($self, $expr) = @_; |
|
76
|
214
|
|
|
|
|
585
|
$self->{MSBE_expr} = $expr; |
|
77
|
214
|
|
|
|
|
1452
|
delete $self->{MSBE_ast}; |
|
78
|
|
|
|
|
|
|
} |
|
79
|
|
|
|
|
|
|
|
|
80
|
|
|
|
|
|
|
### |
|
81
|
|
|
|
|
|
|
### PARSER |
|
82
|
|
|
|
|
|
|
### |
|
83
|
|
|
|
|
|
|
|
|
84
|
|
|
|
|
|
|
my $match_int = MF::INTEGER->_match; |
|
85
|
|
|
|
|
|
|
my $match_float = MF::FLOAT->_match; |
|
86
|
|
|
|
|
|
|
my $match_name = MF::NAME->_match; |
|
87
|
|
|
|
|
|
|
my $match_date = MF::DATE->_match; |
|
88
|
|
|
|
|
|
|
my $match_time = MF::TIME->_match; |
|
89
|
|
|
|
|
|
|
my $match_tz = MF::TIMEZONE->_match; |
|
90
|
|
|
|
|
|
|
my $match_dt = MF::DATETIME->_match; |
|
91
|
|
|
|
|
|
|
my $match_dur = MF::DURATION->_match; |
|
92
|
|
|
|
|
|
|
|
|
93
|
|
|
|
|
|
|
my $match_op = join '|', |
|
94
|
|
|
|
|
|
|
qw{ // -> }, '[?*\/+\-#~.%]', |
|
95
|
|
|
|
|
|
|
qw{ =~ !~ <=> <= >= == != < > }, # order is important |
|
96
|
|
|
|
|
|
|
qw{ :(?![0-9][0-9]) (?<![0-9][0-9]): }, |
|
97
|
|
|
|
|
|
|
( map "$_\\b", qw/ and or not xor exists like unlike cmp lt le eq ne ge gt/ |
|
98
|
|
|
|
|
|
|
); |
|
99
|
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
sub _tokenize($) |
|
101
|
301
|
|
|
301
|
|
6372
|
{ my ($self, $s) = @_; |
|
102
|
301
|
|
|
|
|
589
|
our @t = (); |
|
103
|
301
|
|
|
|
|
496
|
my $parens_open = 0; |
|
104
|
|
|
|
|
|
|
|
|
105
|
28
|
|
|
28
|
|
252
|
use re 'eval'; #XXX needed with newer than 5.16 perls? |
|
|
28
|
|
|
|
|
53
|
|
|
|
28
|
|
|
|
|
56146
|
|
|
106
|
|
|
|
|
|
|
|
|
107
|
301
|
|
|
|
|
33537
|
$s =~ m/ ^ |
|
108
|
|
|
|
|
|
|
(?: \s* |
|
109
|
|
|
|
|
|
|
(?| \# (?: \s [^\n\r]+ | $ ) \ |
|
110
|
47
|
|
|
|
|
171
|
| ( true\b | false\b ) (?{ push @t, MF::BOOLEAN->new($+) }) |
|
111
|
|
|
|
|
|
|
| ( \" (?: \\\" | [^"] )* \" ) |
|
112
|
100
|
|
|
|
|
432
|
(?{ push @t, MF::STRING->new($+) }) |
|
113
|
|
|
|
|
|
|
| ( \' (?: \\\' | [^'] )* \' ) |
|
114
|
5
|
|
|
|
|
30
|
(?{ push @t, MF::STRING->new($+) }) |
|
115
|
39
|
|
|
|
|
228
|
| ( $match_dur ) (?{ push @t, MF::DURATION->new($+) }) |
|
116
|
13
|
|
|
|
|
112
|
| ( $match_tz ) (?{ push @t, MF::TIMEZONE->new($+) }) |
|
117
|
313
|
|
|
|
|
1374
|
| ( $match_op ) (?{ push @t, MF::OPERATOR->new($+) }) |
|
118
|
82
|
|
|
|
|
469
|
| ( $match_name ) (?{ push @t, MF::NAME->new($+) }) |
|
119
|
31
|
|
|
|
|
157
|
| ( $match_dt ) (?{ push @t, MF::DATETIME->new($+) }) |
|
120
|
23
|
|
|
|
|
88
|
| ( $match_date ) (?{ push @t, MF::DATE->new($+) }) |
|
121
|
21
|
|
|
|
|
155
|
| ( $match_time ) (?{ push @t, MF::TIME->new($+) }) |
|
122
|
42
|
|
|
|
|
187
|
| ( $match_float ) (?{ push @t, MF::FLOAT->new($+) }) |
|
123
|
166
|
|
|
|
|
853
|
| ( $match_int ) (?{ push @t, MF::INTEGER->new($+) }) |
|
124
|
5
|
|
|
|
|
36
|
| \( (?{ push @t, MF::PARENS->new('(', ++$parens_open) }) |
|
125
|
5
|
|
|
|
|
12
|
| \) (?{ push @t, MF::PARENS->new(')', $parens_open--) }) |
|
126
|
4
|
|
|
|
|
30
|
| \$ ([1-9][0-9]*) (?{ push @t, MF::CAPTURE->new($+) }) |
|
127
|
|
|
|
|
|
|
| $ |
|
128
|
0
|
|
|
|
|
0
|
| (.+) (?{ error __x"expression '{name}', failed at '{where}'", |
|
129
|
|
|
|
|
|
|
name => $self->name, where => $+ }) |
|
130
|
|
|
|
|
|
|
) |
|
131
|
|
|
|
|
|
|
)+ \z /sxo; |
|
132
|
|
|
|
|
|
|
|
|
133
|
301
|
50
|
|
|
|
1319
|
! $parens_open |
|
134
|
|
|
|
|
|
|
or error __x"expression '{name}', parenthesis do not match", name => $self->name; |
|
135
|
|
|
|
|
|
|
|
|
136
|
301
|
|
|
|
|
1303
|
\@t; |
|
137
|
|
|
|
|
|
|
} |
|
138
|
|
|
|
|
|
|
|
|
139
|
|
|
|
|
|
|
sub _build_ast($$) |
|
140
|
566
|
|
|
566
|
|
1261
|
{ my ($self, $t, $prio) = @_; |
|
141
|
566
|
100
|
|
|
|
2255
|
return shift @$t if @$t < 2; |
|
142
|
|
|
|
|
|
|
|
|
143
|
|
|
|
|
|
|
PROGRESS: |
|
144
|
299
|
|
|
|
|
1152
|
while(my $first = shift @$t) |
|
145
|
|
|
|
|
|
|
{ |
|
146
|
601
|
100
|
|
|
|
2935
|
if($first->isa('MF::PARENS')) |
|
147
|
5
|
|
|
|
|
15
|
{ my $level = $first->level; |
|
148
|
|
|
|
|
|
|
|
|
149
|
5
|
|
|
|
|
8
|
my @nodes; |
|
150
|
5
|
|
|
|
|
15
|
while(my $node = shift @$t) |
|
151
|
25
|
100
|
100
|
|
|
68
|
{ last if $node->isa('MF::PARENS') && $node->level==$level; |
|
152
|
20
|
|
|
|
|
86
|
push @nodes, $node; |
|
153
|
|
|
|
|
|
|
} |
|
154
|
5
|
|
|
|
|
24
|
$first = $self->_build_ast(\@nodes, 0); |
|
155
|
5
|
|
|
|
|
10
|
redo PROGRESS; |
|
156
|
|
|
|
|
|
|
} |
|
157
|
|
|
|
|
|
|
|
|
158
|
596
|
100
|
|
|
|
1471
|
if(ref $first eq 'MF::OPERATOR') # unresolved operator |
|
159
|
38
|
|
|
|
|
108
|
{ my $op = $first->token; |
|
160
|
|
|
|
|
|
|
|
|
161
|
38
|
100
|
100
|
|
|
184
|
if($op eq '#' || $op eq '.') |
|
162
|
|
|
|
|
|
|
{ # Fragments and Methods are always infix, but their left-side arg |
|
163
|
|
|
|
|
|
|
# can be left-out. As PREFIX, they would be RTL but we need LTR |
|
164
|
6
|
|
|
|
|
16
|
unshift @$t, $first; |
|
165
|
6
|
|
|
|
|
18
|
$first = MF::NAME->new(''); |
|
166
|
6
|
|
|
|
|
17
|
redo PROGRESS; |
|
167
|
|
|
|
|
|
|
} |
|
168
|
|
|
|
|
|
|
|
|
169
|
32
|
50
|
|
|
|
144
|
my $next = $self->_build_ast($t, $prio) |
|
170
|
|
|
|
|
|
|
or error __x"expression '{name}', monadic '{op}' not followed by anything useful", |
|
171
|
|
|
|
|
|
|
name => $self->name, op => $op; |
|
172
|
|
|
|
|
|
|
|
|
173
|
32
|
|
|
|
|
115
|
$first = MF::PREFIX->new($op, $next); |
|
174
|
32
|
|
|
|
|
128
|
redo PROGRESS; |
|
175
|
|
|
|
|
|
|
} |
|
176
|
|
|
|
|
|
|
|
|
177
|
558
|
100
|
|
|
|
2583
|
my $next = $t->[0] |
|
178
|
|
|
|
|
|
|
or return $first; # end of expression |
|
179
|
|
|
|
|
|
|
|
|
180
|
291
|
100
|
|
|
|
703
|
if(ref $next ne 'MF::OPERATOR') |
|
181
|
2
|
50
|
|
|
|
10
|
{ if($next->isa('MF::TIMEZONE')) |
|
182
|
|
|
|
|
|
|
{ # Oops, mis-parse |
|
183
|
2
|
|
|
|
|
10
|
unshift @$t, $next->cast('MF::INTEGER'); |
|
184
|
2
|
|
|
|
|
7
|
$next = MF::OPERATOR->new('+'); |
|
185
|
|
|
|
|
|
|
} |
|
186
|
|
|
|
|
|
|
else |
|
187
|
0
|
|
|
|
|
0
|
{ error __x"expression '{name}', expected infix operator but found '{type}'", name => $self->name, type => ref $next; |
|
188
|
|
|
|
|
|
|
} |
|
189
|
|
|
|
|
|
|
} |
|
190
|
|
|
|
|
|
|
|
|
191
|
291
|
|
|
|
|
867
|
my $op = $next->token; |
|
192
|
291
|
50
|
|
|
|
801
|
@$t or error __x"expression '{name}', infix operator '{op}' requires right-hand argument", name => $self->name, op => $op; |
|
193
|
|
|
|
|
|
|
|
|
194
|
291
|
|
|
|
|
917
|
my ($next_prio, $assoc) = MF::OPERATOR->find($op); |
|
195
|
|
|
|
|
|
|
|
|
196
|
291
|
100
|
66
|
|
|
1336
|
return $first |
|
|
|
|
100
|
|
|
|
|
|
197
|
|
|
|
|
|
|
if $next_prio < $prio |
|
198
|
|
|
|
|
|
|
|| ($next_prio==$prio && $assoc==MF::OPERATOR::LTR); |
|
199
|
|
|
|
|
|
|
|
|
200
|
266
|
100
|
|
|
|
666
|
if($op eq ':') |
|
201
|
7
|
|
|
|
|
12
|
{ return $first; |
|
202
|
|
|
|
|
|
|
} |
|
203
|
|
|
|
|
|
|
|
|
204
|
259
|
|
|
|
|
521
|
shift @$t; # apply the operator |
|
205
|
259
|
100
|
|
|
|
595
|
if($op eq '?') |
|
206
|
7
|
|
|
|
|
14
|
{ my $then = $self->_build_ast($t, 0); |
|
207
|
7
|
|
|
|
|
8
|
my $colon = shift @$t; |
|
208
|
7
|
0
|
33
|
|
|
17
|
$colon && $colon->token eq ':' |
|
|
|
50
|
|
|
|
|
|
|
209
|
|
|
|
|
|
|
or error __x"expression '{name}', expected ':' in '?:', but got '{token}'", |
|
210
|
|
|
|
|
|
|
name => $self->name, token => ($next ? $colon->token : 'end-of-line'); |
|
211
|
|
|
|
|
|
|
|
|
212
|
7
|
|
|
|
|
28
|
my $else = $self->_build_ast($t, $next_prio); |
|
213
|
7
|
|
|
|
|
18
|
$first = MF::TERNARY->new($op, $first, $then, $else); |
|
214
|
7
|
|
|
|
|
18
|
redo PROGRESS; |
|
215
|
|
|
|
|
|
|
} |
|
216
|
|
|
|
|
|
|
|
|
217
|
252
|
|
|
|
|
824
|
$first = MF::INFIX->new($op, $first, $self->_build_ast($t, $next_prio)); |
|
218
|
252
|
|
|
|
|
1027
|
redo PROGRESS; |
|
219
|
|
|
|
|
|
|
} |
|
220
|
|
|
|
|
|
|
} |
|
221
|
|
|
|
|
|
|
|
|
222
|
|
|
|
|
|
|
#-------------------- |
|
223
|
|
|
|
|
|
|
|
|
224
|
|
|
|
|
|
|
sub evaluate($) |
|
225
|
279
|
|
|
279
|
1
|
4503
|
{ my ($self, $context, %args) = @_; |
|
226
|
279
|
|
|
|
|
778
|
my $expr = $self->expression; |
|
227
|
|
|
|
|
|
|
|
|
228
|
279
|
50
|
|
|
|
1382
|
my $result |
|
|
|
100
|
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
229
|
|
|
|
|
|
|
= ref $expr eq 'CODE' ? $self->toType($expr->($context, $self, %args)) |
|
230
|
|
|
|
|
|
|
: ! blessed $expr ? $self->tree($expr)->compute($context) |
|
231
|
|
|
|
|
|
|
: $expr->isa('Math::Formula::Type') ? $expr |
|
232
|
|
|
|
|
|
|
: panic; |
|
233
|
|
|
|
|
|
|
|
|
234
|
|
|
|
|
|
|
# For external evaluation calls, we must follow the request |
|
235
|
279
|
|
100
|
|
|
1661
|
my $expect = $args{expect} || $self->returns; |
|
236
|
279
|
100
|
100
|
|
|
2009
|
$result && $expect && ! $result->isa($expect) ? $result->cast($expect, $context) : $result; |
|
237
|
|
|
|
|
|
|
} |
|
238
|
|
|
|
|
|
|
|
|
239
|
|
|
|
|
|
|
|
|
240
|
|
|
|
|
|
|
|
|
241
|
|
|
|
|
|
|
my %_match = map { my $match = $_->_match; ( $_ => qr/^$match$/x ) } |
|
242
|
|
|
|
|
|
|
qw/MF::DATETIME MF::TIME MF::DATE MF::DURATION/; |
|
243
|
|
|
|
|
|
|
|
|
244
|
|
|
|
|
|
|
sub toType($) |
|
245
|
22
|
|
|
22
|
1
|
13110
|
{ my ($self, $data) = @_; |
|
246
|
22
|
100
|
|
|
|
90
|
if(blessed $data) |
|
247
|
8
|
100
|
|
|
|
93
|
{ return $data if $data->isa('Math::Formula::Type'); # explicit type |
|
248
|
4
|
100
|
|
|
|
23
|
return MF::DATETIME->new(undef, $data) if $data->isa('DateTime'); |
|
249
|
3
|
100
|
|
|
|
30
|
return MF::DURATION->new(undef, $data) if $data->isa('DateTime::Duration'); |
|
250
|
2
|
100
|
|
|
|
16
|
return MF::FRAGMENT->new($data->name, $data) if $data->isa('Math::Formula::Context'); |
|
251
|
|
|
|
|
|
|
} |
|
252
|
|
|
|
|
|
|
|
|
253
|
15
|
|
|
0
|
|
70
|
my $match = sub { my $type = shift; my $match = $type->_match; qr/^$match$/ }; |
|
|
0
|
|
|
|
|
0
|
|
|
|
0
|
|
|
|
|
0
|
|
|
|
0
|
|
|
|
|
0
|
|
|
254
|
|
|
|
|
|
|
|
|
255
|
|
|
|
|
|
|
ref $data eq 'SCALAR' ? MF::STRING->new($data) |
|
256
|
|
|
|
|
|
|
: $data =~ /^[+-]?[0-9]+$/ ? MF::INTEGER->new(undef, $data) |
|
257
|
|
|
|
|
|
|
: $data =~ /^[+-]?[0-9]+\./ ? MF::FLOAT->new(undef, $data) |
|
258
|
|
|
|
|
|
|
: $data =~ /^(?:true|false)$/ ? MF::BOOLEAN->new($data) |
|
259
|
|
|
|
|
|
|
: ref $data eq 'Regexp' ? MF::REGEXP->new(undef, $data) |
|
260
|
|
|
|
|
|
|
: $data =~ $_match{'MF::DATETIME'} ? MF::DATETIME->new($data) |
|
261
|
|
|
|
|
|
|
: $data =~ $_match{'MF::TIME'} ? MF::TIME->new($data) |
|
262
|
|
|
|
|
|
|
: $data =~ $_match{'MF::DATE'} ? MF::DATE->new($data) |
|
263
|
15
|
50
|
|
|
|
412
|
: $data =~ $_match{'MF::DURATION'} ? MF::DURATION->new($data) |
|
|
|
100
|
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
264
|
|
|
|
|
|
|
: $data =~ /^(['"]).*\1$/ ? MF::STRING->new($data) |
|
265
|
|
|
|
|
|
|
: error __x"not an expression (string needs \\ ) for '{data}'", data => $data; |
|
266
|
|
|
|
|
|
|
} |
|
267
|
|
|
|
|
|
|
|
|
268
|
|
|
|
|
|
|
#-------------------- |
|
269
|
|
|
|
|
|
|
|
|
270
|
|
|
|
|
|
|
1; |