File Coverage

blib/lib/Syntax/Keyword/Match.pm
Criterion Covered Total %
statement 28 30 93.3
branch 7 10 70.0
condition n/a
subroutine 10 10 100.0
pod 0 3 0.0
total 45 53 84.9


line stmt bran cond sub pod time code
1             # You may distribute under the terms of either the GNU General Public License
2             # or the Artistic License (the same terms as Perl itself)
3             #
4             # (C) Paul Evans, 2021-2023 -- leonerd@leonerd.org.uk
5              
6             package Syntax::Keyword::Match 0.15;
7              
8 19     19   4286785 use v5.14;
  19         81  
9 19     19   115 use warnings;
  19         40  
  19         1218  
10              
11 19     19   118 use Carp;
  19         36  
  19         12171  
12              
13             require XSLoader;
14             XSLoader::load( __PACKAGE__, our $VERSION );
15              
16             =head1 NAME
17              
18             C - a C syntax for perl
19              
20             =head1 SYNOPSIS
21              
22             use v5.16;
23             use Syntax::Keyword::Match;
24              
25             my $n = ...;
26              
27             match($n : ==) {
28             case(1) { say "It's one" }
29             case(2) { say "It's two" }
30             case(3) { say "It's three" }
31             case(4), case(5)
32             { say "It's four or five" }
33             case if($n < 10)
34             { say "It's less than ten" }
35             default { say "It's something else" }
36             }
37              
38             =head1 DESCRIPTION
39              
40             This module provides a syntax plugin that implements a control-flow block
41             called C, which executes at most one of a choice of different
42             blocks depending on the value of its controlling expression.
43              
44             This is similar to C's C syntax (copied into many other
45             languages), or syntax provided by L.
46              
47             This is an initial, experimental implementation. Furthermore, it is built as
48             a non-trivial example use-case on top of L, which is also
49             experimental. No API or compatibility guarantees are made at this time.
50              
51             =head1 Experimental Features
52              
53             Some of the features of this module are currently marked as experimental (even
54             within the context that the module itself is experimental). They will provoke
55             warnings in the C category, unless silenced.
56              
57             use Syntax::Keyword::Match qw( match :experimental(dispatch) );
58              
59             use Syntax::Keyword::Match qw( match :experimental ); # all of the above
60              
61             =cut
62              
63             =head1 KEYWORDS
64              
65             =head2 match
66              
67             match( EXPR : OP ) {
68             ...
69             }
70              
71             A C statement provides the controlling expression, comparison operator,
72             and sequence of C statements for a match operation. The expression is
73             evaluated to yield a scalar value, which is then compared, using the
74             comparison operator, against each of the C labels in the order they are
75             written, topmost first. If a match is found then the body of the labelled
76             block is executed. If no label matches but a C block is present, that
77             will be executed instead. After a single inner block has been executed, no
78             further tests are performed and execution continues from the statement
79             following the C statement.
80              
81             The braces following the C block must only contain C or
82             C statements. Arbitrary code is not supported here.
83              
84             Even though a C statement is a full statement and not an expression, it
85             can still yield a value if it appears as the final statment in its containing
86             C or C block. For example:
87              
88             my $result = do {
89             match( $topic : == ) {
90             case(1) { ... }
91             }
92             };
93              
94             If the controlling expression introduces a new variable, that variable will be
95             visible within any of the C blocks, and will go out of scope after the
96             C statement finishes. This may be useful for temporarily storing the
97             result of a more complex expression.
98              
99             match( my $x = some_function_call() : == ) {
100             case ...
101             }
102              
103             =head3 Comparison Operators
104              
105             The comparison operator must be either C (to compare cases as strings) or
106             C<==> (to compare them as numbers), or C<=~> (to compare cases using regexps).
107              
108             I, or previous versions on Perl
109             releases 5.32 onwards, the C operator is also supported, allowing
110             dispatch based on what type of object the controlling expression gives.
111              
112             match( $obj : isa ) {
113             case(A::Package) { ... }
114             case(Another::Package) { ... }
115             }
116              
117             Remember that comparisons are made in the order they are written, from the top
118             downwards. Therefore, if you list a derived class as well as a base class,
119             make sure to put the derived class B the base class, or instances of
120             that type will also match the base class C block and the derived one
121             will never match.
122              
123             class TheBase {}
124             class Derived :isa(TheBase) {}
125              
126             match( $obj : isa ) {
127             case(TheBase) { ... }
128             case(Derived) {
129             # This case will never match as the one above will always happen first
130             }
131             }
132              
133             I the operator syntax is parsed using L,
134             meaning that custom infix operators can be recognised, even on versions of
135             perl that do not support the full C mechanism.
136              
137             =head2 case
138              
139             case(VAL) { STATEMENTS... }
140              
141             case(VAL), case(VAL), ... { STATEMENTS... }
142              
143             A C statement must only appear inside the braces of a C. It
144             provides a block of code to run if the controlling expression's value matches
145             the value given in the C statement, according to the comparison
146             operator.
147              
148             Multiple C statements are permitted for a single block. A value matching
149             any of them will run the code inside the block.
150              
151             If the value is a non-constant expression, such as a variable or function
152             call, it will be evaluated as part of performing the comparison every time the
153             C statement is executed. For best performance it is advised to extract
154             values that won't need computing again into a variable or C that
155             can be calculated just once at program startup; for example:
156              
157             use constant CONDITION => a_function("with", "arguments");
158              
159             match( $var : eq ) {
160             case(CONDITION) { ... }
161             ...
162             }
163              
164             The C<:experimental(dispatch)> feature selects a more efficient handling of
165             sequences of multiple C blocks with constant expressions. This handling
166             is implemented with a custom operator that will entirely confuse modules like
167             C or optree inspectors like coverage tools so is not selected by
168             default, but can be enabled for extra performance in critical sections.
169              
170             =head2 case if
171              
172             case if(EXPR) { STATEMENTS... }
173              
174             case(VAL), case if(EXPR) { STATEMENTS... }
175              
176             I
177              
178             A C statement may also be written C with a boolean predicate
179             expression in parentheses. This inserts a direct boolean test into the
180             comparison logic, allowing for other logical tests that aren't easily
181             expressed as uses of the comparison operator. As C is an alternative
182             to a regular C, they can be combined on a single code block if required.
183              
184             For example, when testing an inequality in a selection of numerical C<==>
185             tests, or a single regexp test among some string C tests.
186              
187             match( $num : == ) {
188             case(0) { ... }
189             case(1), case(2) { ... }
190             case if($num < 5) { ... }
191             }
192              
193             Z<>
194              
195             match( $str : eq ) {
196             case("abc") { ... }
197             case("def") { ... }
198             case if($str =~ m/g/) { ... }
199             }
200              
201             By default the match value is not assigned into a variable that is visible
202             to C expressions, but if needed a new lexical can be constructed by
203             using a regular C assignment.
204              
205             match( my $v = some_expression() : eq ) {
206             case if($v =~ m/pattern/) { ... }
207             }
208              
209             =head2 default
210              
211             A C statement must only appear inside the braces of a C. If
212             present, it must be the final choice, and there must only be one of them. It
213             provides a block of code to run if the controlling expression's value did not
214             match any of the given C labels.
215              
216             =cut
217              
218             =head1 COMPARISONS
219              
220             As this syntax is fairly similar to a few other ideas, the following
221             comparisons may be useful.
222              
223             =head2 Core perl's given/when syntax
224              
225             Compared to core perl's C syntax (available with
226             C), this syntax is initially visually very similar but
227             actually behaves very differently. Core's C uses the smartmatch
228             (C<~~>) operator for its comparisons, which is complex, subtle, and hard to
229             use correctly - doubly-so when comparisons against values stored in variables
230             rather than literal constants are involved. It can be unpredictable whether
231             string or numerical comparison are being used, for example. By comparison,
232             this module requires the programmer to specify the comparison operator. The
233             choice of string or numerical comparison is given in the source code - there
234             can be no ambiguity.
235              
236             Additionally, the C operator is also permitted, which has no equivalent
237             ability in smartmatch.
238              
239             Also, the C syntax permits mixed code within a C block
240             which is run unconditionally, or at least, until the first successful C
241             statement is encountered. The syntax provided by this module requires that the
242             only code inside a C block be a sequence of C statements. No
243             other code is permitted.
244              
245             =head2 Switch::Plain
246              
247             Like this module, L also provides a syntax where the programmer
248             specifies whether the comparison is made using stringy or numerical semantics.
249             C also permits additional conditions to be placed on C
250             blocks, whereas this module does not.
251              
252             Additionally, the C operator is also permitted, which has no equivalent
253             ability in C.
254              
255             =head2 C's switch/case
256              
257             The C programming language provides a similar sort of syntax, using keywords
258             named C and C. One key difference between that and the syntax
259             provided for Perl by this module is that in C the C labels really are
260             just labels. The C part of the statement effectively acts as a sort of
261             computed C. This often leads to bugs caused by forgetting to put a
262             C at the end of a sequence of statements before the next C label;
263             a situation called "fallthrough". Such a mistake is impossible with this
264             module, because every C is provided by a block. Once execution has
265             finished with the block, the entire C statement is finished. There is
266             no possibility of accidental fallthrough.
267              
268             C's syntax only permits compiletime constants for C labels, whereas this
269             module will also allow the result of any runtime expression.
270              
271             Code written in C will perform identically even if any of the C labels
272             and associated code are moved around into a different order. The syntax
273             provided by this module notionally performs all of its tests in the order they
274             are written in, and any changes of that order might cause a different result.
275              
276             =cut
277              
278             sub import
279             {
280 15     15   219 my $pkg = shift;
281 15         47 my $caller = caller;
282              
283 15         61 $pkg->import_into( $caller, @_ );
284             }
285              
286             sub unimport
287             {
288 1     1   11 my $pkg = shift;
289 1         3 my $caller = caller;
290              
291 1         5 $pkg->unimport_into( $caller, @_ );
292             }
293              
294             my @EXPERIMENTAL = qw( dispatch );
295              
296 18     18 0 118 sub import_into { shift->apply( sub { $^H{ $_[0] }++ }, @_ ) }
  15     15   114  
297 1     1 0 6 sub unimport_into { shift->apply( sub { delete $^H{ $_[0] } }, @_ ) }
  1     1   6  
298              
299             sub apply
300             {
301 16     16 0 35 my $pkg = shift;
302 16         56 my ( $cb, $caller, @syms ) = @_;
303              
304 16 100       138 @syms or @syms = ( "match" );
305              
306 16         51 my %syms = map { $_ => 1 } @syms;
  19         113  
307              
308 16 50       103 $cb->( "Syntax::Keyword::Match/match" ) if delete $syms{match};
309              
310 16         57 foreach ( @EXPERIMENTAL ) {
311 16 100       110 $cb->( "Syntax::Keyword::Match/experimental($_)" ) if delete $syms{":experimental($_)"};
312             }
313              
314 16 50       59 if( delete $syms{":experimental"} ) {
315 0         0 $cb->( "Syntax::Keyword::Match/experimental($_)" ) for @EXPERIMENTAL;
316             }
317              
318 16 50       25203 croak "Unrecognised import symbols @{[ keys %syms ]}" if keys %syms;
  0            
319             }
320              
321             =head1 TODO
322              
323             This is clearly an early experimental work. There are many features to add,
324             and design decisions to make. Rather than attempt to list them all here it
325             would be best to check the RT bug queue at
326              
327             L
328              
329             =head1 AUTHOR
330              
331             Paul Evans
332              
333             =cut
334              
335             0x55AA;