File Coverage

blib/lib/Math/SymbolicX/ParserExtensionFactory.pm
Criterion Covered Total %
statement 57 61 93.4
branch 12 22 54.5
condition 3 7 42.8
subroutine 11 12 91.6
pod 1 1 100.0
total 84 103 81.5


line stmt bran cond sub pod time code
1             package Math::SymbolicX::ParserExtensionFactory;
2              
3 4     4   465715 use 5.006;
  4         19  
  4         148  
4 4     4   23 use strict;
  4         8  
  4         121  
5 4     4   19 use warnings;
  4         8  
  4         129  
6 4     4   20 use Carp;
  4         7  
  4         339  
7 4     4   846 use Math::Symbolic;
  4         149083  
  4         1524  
8 4     4   1369 use Text::Balanced;
  4         8  
  4         3241  
9              
10             our $BeenUsedBefore = {};
11             our $Functions = {};
12             our $Order = [];
13             our $RegularExpression = qr/(?!)/;
14              
15             our $VERSION = '3.02';
16              
17             sub import {
18 6     6   66535 my $package = shift;
19 6 50       37 croak("Uneven number of arguments in usage of "
20             . "Math::SymbolicX::ParserExtensionFactory")
21             if @_ % 2;
22              
23 6         19 my %args = @_;
24              
25 6         34 _extend_parser();
26              
27 6         34 foreach my $key ( keys %args ) {
28 3 50       17 croak("Invalid keys => value pairs as arguments in usage of "
29             . "Math::SymbolicX::ParserExtensionFactory")
30             if not ref( $args{$key} ) eq 'CODE';
31 3 50       13 if ( not exists $Functions->{$key} ) {
32 3         11 push @$Order, $key;
33             }
34 3         13 $Functions->{$key} = $args{$key};
35             }
36              
37 6         23 $RegularExpression = _regenerate_regex($Order);
38              
39 6         5265 return ();
40             }
41              
42             sub _extend_parser {
43              
44 6     6   14 my $parser = shift;
45 6 50       24 $parser = $Math::Symbolic::Parser if not defined $parser;
46              
47             # make sure there is a parser
48 6 50       26 if (not defined $parser) {
49 0         0 $parser = $Math::Symbolic::Parser = Math::Symbolic::Parser->new();
50             }
51              
52 6 100       50 if ( not exists $BeenUsedBefore->{"$parser"} ) {
53 3 50       31 if ($parser->isa('Parse::RecDescent')) {
    0          
54 3         11 _extend_parser_recdescent($parser)
55             }
56             elsif ($parser->isa('Math::Symbolic::Parser::Yapp')) {
57 0         0 _extend_parser_yapp($parser);
58             }
59             else {
60 0         0 die "Unsupported parser type!";
61             }
62 3         19 $BeenUsedBefore->{"$parser"} = 1;
63             }
64             }
65              
66             sub _extend_parser_yapp {
67             # This is a no-op since ::Parser::Yapp has built-in support for
68             # ::ParserExtensionFactory. This would probably not be possible
69             # otherwise.
70 0     0   0 return(1);
71             }
72              
73             sub _extend_parser_recdescent {
74 3     3   5 my $parser = shift;
75 3         12 $parser->{__PRIV_EXT_FUNC_REGEX} = qr/(?!)/;
76 3         22 $parser->Extend(<<'EXTENSION');
77             function: /$thisparser->{__PRIV_EXT_FUNC_REGEX}\s*(?=\()/ {extract_bracketed($text, '(')}
78             {
79             warn 'function_private_msx_parser_extension_factory '
80             if $Math::Symbolic::Parser::DEBUG;
81             my $function = $item[1];
82             $function =~ s/\s+$//;
83             my $argstring = substr($item[2], 1, length($item[2])-2);
84             die "Invalid extension function and/or arguments '$function$item[2]' ".
85             "(Math::SymbolicX::ParserExtensionFactory)"
86             if not exists
87             $thisparser->{__PRIV_EXT_FUNCTIONS}{$function};
88             my $result = $thisparser->{__PRIV_EXT_FUNCTIONS}{$function}->($argstring);
89             die "Invalid result of extension function application "
90             ."('$item[1]($argstring)'). Also refer to the "
91             ."Math::SymbolicX::ParserExtensionFactory manpage."
92             if ref($result) !~ /^Math::Symbolic/;
93             $return = $result;
94             }
95              
96             | /$Math::SymbolicX::ParserExtensionFactory::RegularExpression\s*(?=\()/ {extract_bracketed($text, '(')}
97             {
98             warn 'function_global_msx_parser_extension_factory '
99             if $Math::Symbolic::Parser::DEBUG;
100             my $function = $item[1];
101             $function =~ s/\s+$//;
102             my $argstring = substr($item[2], 1, length($item[2])-2);
103             die "Invalid extension function and/or arguments '$function$item[2]' ".
104             "(Math::SymbolicX::ParserExtensionFactory)"
105             if not exists
106             $Math::SymbolicX::ParserExtensionFactory::Functions->{$function};
107             my $result = $Math::SymbolicX::ParserExtensionFactory::Functions->{$function}->($argstring);
108             die "Invalid result of extension function application "
109             ."('$item[1]($argstring)'). Also refer to the "
110             ."Math::SymbolicX::ParserExtensionFactory manpage."
111             if ref($result) !~ /^Math::Symbolic/;
112             $return = $result;
113             }
114              
115             EXTENSION
116 3         86212 return(1);
117             }
118              
119             sub _regenerate_regex {
120 8     8   22 my @arrays = @_;
121 8         21 my $string = join '|', map {"\Q$_\E"} map {@$_} @arrays;
  6         29  
  8         29  
122 8 100       41 return qr/(?!)/ if $string eq '';
123 6         94 return qr/(?:$string)/;
124             }
125              
126             sub add_private_functions {
127 2 50 33 2 1 16317 shift if not ref $_[0] and $_[0] eq __PACKAGE__;
128 2         4 my $parser = shift;
129 2 50       9 croak("Invalid number of arguments!") if @_ % 2;
130              
131 2   50     20 $parser->{__PRIV_EXT_FUNCTIONS} ||= {};
132 2   50     13 $parser->{__PRIV_EXT_FUNC_ORDER} ||= [];
133 2         7 while (@_) {
134 2         22 my $name = shift;
135 2         5 push @{$parser->{__PRIV_EXT_FUNC_ORDER}}, $name;
  2         5  
136 2         11 $parser->{__PRIV_EXT_FUNCTIONS}{$name} = shift;
137             }
138              
139 2         8 $parser->{__PRIV_EXT_FUNC_REGEX} = _regenerate_regex( $parser->{__PRIV_EXT_FUNC_ORDER} );
140             }
141              
142             1;
143             __END__
144              
145             =head1 NAME
146              
147             Math::SymbolicX::ParserExtensionFactory - Generate parser extensions
148              
149             =head1 SYNOPSIS
150              
151             use Math::Symbolic qw/parse_from_string/;
152            
153             # This will extend all parser objects in your program:
154             use Math::SymbolicX::ParserExtensionFactory (
155            
156             functionname => sub {
157             my $argumentstring = shift;
158             my $result = construct_some_math_symbolic_tree( $argumentstring );
159             return $result;
160             },
161            
162             anotherfunction => sub {
163             ...
164             },
165            
166             );
167            
168             # ...
169             # Later in your code
170            
171             my $formula = parse_from_string('variable * 4 * functionname(someargument)');
172            
173             # use $formula as a Math::Symbolic object.
174             # Refer to Math::SymbolicX::BigNum (arbitrary precision arithmetic
175             # support through the Math::Big* modules) or to
176             # Math::SymbolicX::ComplexNumbers (complex number support) for examples.
177            
178            
179             # Alternative: modify a single parser object only:
180             my $parser = Math::Symbolic::Parser->new();
181            
182             Math::SymbolicX::ParserExtensionFactory->add_private_functions(
183             $parser,
184             fun_function => sub {...},
185             my_function => sub {...},
186             ...
187             );
188              
189             =head1 DESCRIPTION
190              
191             This module provides a simple way to extend the Math::Symbolic parser with
192             arbitrary functions that return any valid Math::Symbolic tree.
193             The return value of the function call is
194             inserted into the complete parse tree at the point at which the function
195             call is parsed. Familiarity with the Math::Symbolic module will be
196             assumed throughout the documentation.
197              
198             This module is not object oriented. It does not export anything. You should
199             not call any subroutines directly nor should you modify any class data
200             directly. The complete interface is the call to
201             C<use Math::SymbolicX::ParserExtensionFactory> and its arguments. The reason
202             for the long module name is that you should not have to call it multiple times
203             in your code because it modifies the parser for good. It is intended to be
204             a pain to type. :-)
205              
206             The aim of the module is to allow for hooks into the parser without modifying
207             the parser yourself because that requires rather in-depth knowledge of the
208             module code. By specifying key => value pairs of function names and
209             function implementations (code references) as arguments to the use() call
210             of the module, this module extends the parser that is stored in the
211             C<$Math::Symbolic::Parser> variable with the specified functions and whenever
212             "C<yourfunction(any argument string with balanced parenthesis)>" occurs
213             in the code, the subroutine reference is called with the argument string as
214             argument.
215              
216             The subroutine is expected to return any Math::Symbolic tree. That means,
217             as of version 0.506 of Math::Symbolic, a Math::Symbolic::Operator, a
218             Math::Symbolic::Variable,
219             or a Math::Symbolic::Constant object. The returned object will be incorporated
220             into the Math::Symbolic tree that results from the parse at the exact position
221             at which the custom function call was parsed.
222              
223             Please note that the usage of this module will be quite slow at compile time
224             because it has to regenerate the complete Math::Symbolic parser the first
225             time you use this module in your code. The run time performance penalty
226             should be low, however.
227              
228             =head1 FUNCTIONS
229              
230             =head2 add_private_functions
231              
232             Callable as class method or function. First argument must be the parser
233             object to modify (either a Parse::RecDescent or a Parse::Yapp based
234             Math::Symbolic parser), followed by key/value pairs of function names
235             and code refs (implementations).
236              
237             Modifies only the parser passed in as first argument. For an example,
238             see synopsis above.
239              
240             =head1 CAVEATS
241              
242             Since version 2.00 of this module, the old, broken parsing of the argument
243             string which would fail on nested, unescaped parenthesis was replaced
244             by a better routine which will correctly parse nested pairs of parenthesis.
245              
246             On the flip side, if the argument string contains unmatched parenthesis,
247             the parse will fail. Examples:
248              
249             "myfunction(foo(bar)" # fails because missing closing parenthesis
250              
251             Escaping of parenthesis in the argument string B<is no longer supported>.
252              
253             =head1 AUTHOR
254              
255             Copyright (C) 2003-2009 Steffen Mueller
256              
257             This library is free software; you can redistribute it and/or modify
258             it under the same terms as Perl itself.
259              
260             You may contact the author at symbolic-module at steffen-mueller dot net
261              
262             Please send feedback, bug reports, and support requests to the Math::Symbolic
263             support mailing list:
264             math-symbolic-support at lists dot sourceforge dot net. Please
265             consider letting us know how you use Math::Symbolic. Thank you.
266              
267             If you're interested in helping with the development or extending the
268             module's functionality, please contact the developers' mailing list:
269             math-symbolic-develop at lists dot sourceforge dot net.
270              
271             =head1 SEE ALSO
272              
273             New versions of this module can be found on
274             http://steffen-mueller.net or CPAN.
275              
276             Also have a look at L<Math::Symbolic>,
277             and at L<Math::Symbolic::Parser>
278              
279             Refer to L<Math::SymbolicX::BigNum> (arbitrary precision
280             arithmetic support through the Math::Big* modules) or to
281             L<Math::SymbolicX::ComplexNumbers> (complex number support) for examples.
282              
283             =cut