File Coverage

blib/lib/Inline/BC.pm
Criterion Covered Total %
statement 80 176 45.4
branch 10 50 20.0
condition 5 14 35.7
subroutine 21 42 50.0
pod 2 9 22.2
total 118 291 40.5


line stmt bran cond sub pod time code
1             package Inline::BC;
2 3     3   50351 use strict;
  3         7  
  3         123  
3 3     3   17 use Carp;
  3         7  
  3         282  
4             require Inline;
5             require DynaLoader;
6             require Exporter;
7 3     3   26 use vars qw(@ISA $VERSION @EXPORT_OK $RUN_ONCE);
  3         5  
  3         235  
8 3     3   14 use vars qw($RE_balanced_brackets $RE_balanced_parens);
  3         4  
  3         119  
9 3     3   15 use vars qw($use_math_lib);
  3         5  
  3         159  
10              
11             $use_math_lib = 0; # assume BC's math library is not required
12             $VERSION = '0.08';
13             @ISA = qw(Inline DynaLoader Exporter);
14              
15 3     3   13 use Cwd qw(abs_path);
  3         5  
  3         128  
16              
17 3     3   3149 use Data::Dumper;
  3         21261  
  3         5204  
18              
19             my @export_ok = qw(bc_init bc_parse bc_run);
20              
21             #==============================================================================
22             # lots of this code has been shamelessly stolen from Inline::Ruby :-)
23             #==============================================================================
24              
25 3     3 1 1222 sub dl_load_flags { 0x01 }
26             eval_support_code();
27              
28              
29             #==============================================================================
30             # Prep the BC interpreter
31             #==============================================================================
32             sub eval_support_code{
33 3 50   3 0 14 return if $RUN_ONCE;
34 3         498 Inline::BC->bootstrap($VERSION);
35             }
36              
37              
38              
39             #==============================================================================
40             # Register BC.pm as a valid Inline language
41             #==============================================================================
42             sub register {
43             return {
44 0     0 0 0 language => 'BC',
45             aliases => ['bc', 'Bc'],
46             type => 'interpreted',
47             suffix => 'bc',
48             };
49             }
50              
51             #==============================================================================
52             # Validate the BC config options
53             #==============================================================================
54             sub validate {
55 5     5 0 16092 my $o = shift;
56              
57 5   50     74 $o->{ILSM} ||= {};
58 5   50     35 $o->{ILSM}{FILTERS} ||= [];
59 5   50     25 $o->{ILSM}{built} ||= 0;
60 5   50     22 $o->{ILSM}{loaded} ||= 0;
61            
62 5         12 $o->{ILSM}{bindto} = [qw(functions)];
63              
64 5         20 while (@_) {
65 1         2 my ($key, $value) = (shift, shift);
66              
67 1 50       5 if ($key eq 'FILTERS') {
    50          
68 0 0 0     0 next if $value eq '1' or $value eq '0'; # ignore ENABLE, DISABLE
69 0 0       0 $value = [$value] unless ref($value) eq 'ARRAY';
70 0         0 my %filters;
71 0         0 for my $val (@$value) {
72 0 0       0 if (ref($val) eq 'CODE') {
73 0         0 $o->add_list($o->{ILSM}, $key, $val, []);
74             }
75             else {
76 0         0 eval { require Inline::Filters };
  0         0  
77 0 0       0 croak "'FILTERS' option requires Inline::Filters to be installed."
78             if $@;
79 0 0       0 %filters = Inline::Filters::get_filters($o->{API}{language})
80             unless keys %filters;
81 0 0       0 if (defined $filters{$val}) {
82 0         0 my $filter = Inline::Filters->new($val,
83             $filters{$val});
84 0         0 $o->add_list($o->{ILSM},
85             $key,
86             [ $filter ],
87             [ ]);
88             }
89             else {
90 0         0 croak "Invalid filter $val specified.";
91             }
92             }
93             }
94             }
95             elsif ($key eq 'MATH_LIB') {
96 1 50 33     5 croak "Invalid value specified for config option 'MATH_LIB'"
97             if ($value ne '1' and $value ne '0');
98              
99 1 50       2 if ($RUN_ONCE) {
100 0 0       0 if ($value ne $use_math_lib) {
101 0         0 warn("The BC interpreter was initialised with option ",
102             "MATH_LIB => $use_math_lib.\n",
103             "Any subsequent use of option 'MATH_LIB' will ",
104             "have no effect.\n");
105             }
106             }
107             else {
108 1         3 $use_math_lib = $value;
109             }
110             }
111             else {
112 0         0 croak "$key is not a valid config option for BC";
113             }
114             }
115              
116             # Ensure the BC interpreter is initialised only once!
117 5 100       16 unless ($RUN_ONCE) {
118 3         241 bc_init( $use_math_lib );
119 3         15 $RUN_ONCE = 1;
120             }
121             }
122              
123              
124             sub usage_validate {
125 0     0 0 0 return "Invalid value for config option $_[0]";
126             }
127              
128              
129             sub add_list {
130 0     0 0 0 my $o = shift;
131 0         0 my ($ref, $key, $value, $default) = @_;
132 0 0       0 $value = [$value] unless ref $value;
133 0 0       0 croak usage_validate($key) unless ref($value) eq 'ARRAY';
134 0         0 for (@$value) {
135 0 0       0 if (defined $_) {
136 0         0 push @{$ref->{$key}}, $_;
  0         0  
137             }
138             else {
139 0         0 $ref->{$key} = $default;
140             }
141             }
142             }
143              
144              
145             #==========================================================================
146             # Print a short information section if PRINT_INFO is enabled.
147             #==========================================================================
148             sub info {
149 0     0 1 0 my $o = shift;
150 0         0 my $info = "";
151              
152 0 0       0 $o->build unless $o->{ILSM}{built};
153              
154 0 0       0 my @functions = @{$o->{ILSM}{namespace}{functions}||[]};
  0         0  
155 0 0       0 $info .= "The following BC functions have been bound to Perl:\n"
156             if @functions;
157 0         0 for my $function (sort @functions) {
158 0         0 $info .= "\tdefine $function()\n";
159             }
160              
161 0 0       0 $info .= "\nThe BC math library has been loaded.\n" if ($use_math_lib);
162              
163 0         0 return $info;
164             }
165              
166              
167             #==========================================================================
168             # Run the code, study the main namespace, and cache the results.
169             #==========================================================================
170             sub build {
171 0     0 0 0 my $o = shift;
172 0 0       0 return if $o->{ILSM}{built};
173              
174             # Filter the code
175 0         0 $o->{ILSM}{code} = $o->filter(@{$o->{ILSM}{FILTERS}});
  0         0  
176              
177 0         0 my $code = $o->{ILSM}{code};
178              
179             # get the function signatures
180             # These regular expressions were derived from Regexp::Common v0.01.
181 0         0 my $RE_comment_C = q{(?:(?:\/\*)(?:(?:(?!\*\/)[\s\S])*)(?:\*\/))};
182 0         0 my $RE_comment_Cpp = q{(?:\/\*(?:(?!\*\/)[\s\S])*\*\/|\/\/[^\n]*\n)};
183 0         0 my $RE_quoted = (q{(?:(?:\")(?:[^\\\"]*(?:\\.[^\\\"]*)*)(?:\")}
184             .q{|(?:\')(?:[^\\\']*(?:\\.[^\\\']*)*)(?:\'))});
185             $RE_balanced_brackets =
186 0         0 qr'(?:[{]((?:(?>[^{}]+)|(??{$RE_balanced_brackets}))*)[}])';
187             $RE_balanced_parens =
188 0         0 qr'(?:[(]((?:(?>[^()]+)|(??{$RE_balanced_parens}))*)[)])';
189              
190             # First, we crush out anything potentially confusing.
191             # The order of these _does_ matter.
192 0         0 $code =~ s/$RE_comment_C/ /go;
193 0         0 $code =~ s/$RE_comment_Cpp/ /go;
194 0         0 $code =~ s/^\#.*(\\\n.*)*//mgo;
195 0         0 $code =~ s/^[\n\s]+//s;
196 0         0 $code =~ s/[\s\n]+$//s;
197 0         0 $code =~ s/\n(\s*)?\n/\n/sg;
198 0         0 $code =~ s/$RE_balanced_brackets/{ }/go;
199 0         0 $code =~ s/\n//sg;
200 0         0 my %functions = ();
201 0         0 while ( $code =~ /define\s+(\w+)\s*?\((.*?)\)\s*?\{.*?\}/gs ){
202 0         0 $functions{$1} = [ split(/,\s*?/, $2) ];
203             }
204 0         0 my $bytecode = bc_parse($o->{ILSM}{code});
205              
206 0         0 my $binding = "";
207 0         0 foreach my $func ( keys %functions ){
208 0         0 my $bcfunc = $func." ( ";
209 0         0 $bcfunc .= join(", ", map { "\$".$_ }(@{$functions{$func}}));
  0         0  
  0         0  
210 0         0 $bcfunc .= " )";
211 0         0 $binding .= <
212             sub $func {
213             # my \$self = shift;
214             END
215 0         0 $binding .= join("", map { " my \$".$_." = shift;\n" }(@{$functions{$func}}));
  0         0  
  0         0  
216 0         0 $binding .= <
217             return &Inline::BC::bc_run( &Inline::BC::bc_parse("$bcfunc\\n") );
218             }
219             END
220             }
221              
222             # Cache the results
223 0         0 require Inline::denter;
224 0         0 my $namespace = Inline::denter->new->indent(
225             *functions => \%functions,
226             *filtered => $o->{ILSM}{code},
227             *bytecode => $bytecode,
228             *binding => "package ".$o->{API}{pkg}.";\n".$binding,
229             );
230              
231 0         0 $o->mkpath("$o->{API}{install_lib}/auto/$o->{API}{modpname}");
232              
233 0 0       0 open BCDAT, "> $o->{API}{location}" or
234             croak "Inline::BC couldn't write parse information!";
235 0         0 print BCDAT $namespace;
236 0         0 close BCDAT;
237              
238 0         0 $o->{ILSM}{namespace} = \%functions;
239 0         0 $o->{ILSM}{built}++;
240              
241             }
242              
243              
244             #==============================================================================
245             # Load the code, run it, and bind everything to Perl
246             #==============================================================================
247             sub load {
248 5     5 0 17 my $o = shift;
249 5 50       19 return if $o->{ILSM}{loaded};
250              
251             # Load the code
252 5 50       283 open BCDAT, $o->{API}{location} or
253             croak "Couldn't open parse info!";
254 5         10768 my $bcdat = join '', ;
255 5         560 close BCDAT;
256              
257 5         47 require Inline::denter;
258 5         41 my %bcdat = Inline::denter->new->undent($bcdat);
259 5         55689 $o->{ILSM}{namespace} = $bcdat{functions};
260 5         14 $o->{ILSM}{code} = $bcdat{filtered};
261 5         13 $o->{ILSM}{binding} = $bcdat{binding};
262 5         40 $o->{ILSM}{loaded}++;
263              
264             # Run it
265 5         41402 bc_run(bc_parse($o->{ILSM}{code}));
266              
267 5     1   1255 eval $o->{ILSM}{binding};
  1     0   118336  
  0     0   0  
  0     0   0  
  0     0   0  
  0     0   0  
  0     0   0  
  0     0   0  
  0     0   0  
  0     0   0  
  0     0   0  
  0     0   0  
  0     0   0  
  0     0   0  
  0     0   0  
  0     0   0  
  0     1   0  
  1     0   142  
  1     1   919  
  0     1   0  
  1     120   307  
  1     240   116  
  1     120   41  
  1     120   2  
  1     120   48  
  120     120   43080  
  120         158  
  120         44123  
  240         96653  
  240         281  
  240         311  
  240         105110  
  120         55222  
  120         172  
  120         144011  
  120         82931  
  120         196  
  120         75744  
  120         79961  
  120         173  
  120         164306  
  120         57836  
  120         188  
  120         43788  
268 5 50       172 croak $@ if $@;
269             }
270              
271             #==============================================================================
272              
273              
274              
275             =head1 NAME
276              
277             Inline::BC - Inline ILSM for bc the arbitrary precision math Language
278              
279             =head1 SYNOPSIS
280              
281             use Inline BC;
282             print x(int(rand(time())));
283             __DATA__
284             __BC__
285             define x(a){
286             scale = 20;
287             return (a*3.456789);
288             }
289            
290              
291             =head1 DESCRIPTION
292              
293             Inline::BC is an ILSM (Inline Support Language Module ) for Gnu bc, the arbitrary
294             precision numeric processing language. Inline::BC - like other ILSMs - allows you
295             to compile (well - render to byte code ), and run Gnu bc code within your Perl
296             program.
297              
298             From the Gnu BC README:
299              
300             bc is an arbitrary precision numeric processing language. Syntax is
301             similar to C, but differs in many substantial areas. It supports
302             interactive execution of statements. bc is a utility included in the
303             POSIX P1003.2/D11 draft standard.
304              
305             This version was written to be a POSIX compliant bc processor with
306             several extensions to the draft standard. Option flags are available
307             to cause warning or rejection of the extensions to the POSIX standard.
308             For those who want only POSIX bc with no extensions, a grammar is
309             provided for exactly the language described in the POSIX document.
310             The grammar (sbc.y) comes from the POSIX document. The Makefile
311             contains rules to make sbc. (for Standard BC)
312              
313             "end of quote"
314              
315             Further documentation about Gnu bc can be found at:
316             http://www.gnu.org/software/bc/bc.html
317             http://www.gnu.org/manual/bc/html_mono/bc.html
318              
319              
320             one thing to note is that you should be careful with setting the global
321             bc parameters like ibase, obase, scale etc. You should not set these in
322             the global code - instead, set them in each function, to avoid the chaos
323             that would follow.
324              
325             Looking at the test suite - there are examples of several different ways of
326             invoking Inline::BC:
327              
328             (1) code in the DATA statement
329             use Inline BC;
330             print x(4) == 5.3 ? "ok 2\n" : "not ok 2\n";
331             __DATA__
332             __BC__
333             define x (a) {
334             scale = 20
335             return (a * 1.5);
336             }
337              
338             (2) inline code with here document
339             use Inline BC => <<'END_BC';
340             define z (a, b) {
341             scale = 6
342             t = a * .357;
343             t = b / t;
344             return ( t );
345             }
346             END_BC
347             print z(4, 7) > 4 ? "ok 3\n" : "not ok 3\n";
348              
349             (3) code in an external file
350             use Inline BC => './tools/test.dat';
351             print aa() =~ /[0\n]/s ? "ok 4\n" : "not ok 4\n";
352              
353              
354             =head1 CONFIG OPTIONS
355              
356             Inline::BC provides the following config options.
357              
358             B 0|1>
359              
360             When Inline::BC is invoked with the config option 'MATH_LIB => 1',
361             then the GNU bc processor is initialised with its builtin math
362             library.
363             The math library offers the following builtin functions:
364              
365             =over 4
366              
367             =item s(x)
368              
369             The sine of x, x is in radians.
370              
371             =item c(x)
372              
373             The cosine of x, x is in radians.
374              
375             =item a(x)
376              
377             The arctangent of x, arctangent returns radians.
378              
379             =item l(x)
380              
381             The natural logarithm of x.
382              
383             =item e(x)
384              
385             The exponential function, raising e to the value x.
386              
387             =item j(n,x)
388              
389             The Bessel function of integer order n of x.
390              
391             =back
392              
393             Example: Calculating the hyperbolic sine of a value.
394              
395             use Inline BC => "DATA", MATH_LIB => 1;
396              
397             my $r = bc_sinh(4.712);
398              
399             __END__